Compare commits

...
Author SHA1 Message Date
Stefan MaierandClaude Opus 5.5 99d7c924db Bots: a person joining a lobby the bots fill takes a bot's place
The bots fill the game up to its maximum; a machine joining the lobby
keeps its place (no bot added for it) and its player takes the last
bot's; the game lists as open while a bot could leave.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 18:00:31 +02:00
Stefan MaierandClaude Opus 5.5 57d96e4681 VR PC menus: fixes from the first Frame test
- bots: no more than the game's MAXIMUM PLAYERS has room for beside the
  people (instead of raising it); the host options show and change the
  bots there are
- VR options hint on the PC main menu drawn whole (left-aligned)
- the online and host panels: a click beside them or on their key's B
  closes them, on the online panel's X enters a code
- spinners' arrows (drawn just outside them) clickable
- Android flat (the emulator): the mouse drives the menus' pointer, for
  testing the menus' clicks with adb shell input mouse
- Frame launcher: Lepton's logcat of the game's container in logcat.txt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 17:45:41 +02:00
Stefan MaierandClaude Opus 5.5 177ad4d40d VR: the PC version's menus (vr.menus, on by default)
- menu_tags.c builds the PC menus in VR too unless vr.menus = "xbox"
- their text fields (Server Setup's game name) typed on the Xbox's
  on-screen keyboard in VR
- join code / online panel on the PC Server Browser (X or click its hint),
  host options (Y or click) and join code on the PC lobby, VR options on
  the PC main menu
- bots: room made for them beside the people (Server Setup's maximum
  players), none in co-op
- player_name_valid accepts trailing spaces (Xbox menus pad new profile
  names, "New001     ", which the PC lobby refused)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 17:17:16 +02:00
Stefan MaierandClaude Opus 5.5 bfb668d498 Merge branch 'merge/upstream-2026-10-05'
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 17:00:26 +02:00
Stefan MaierandClaude Opus 5.5 fc2b6551ca Merge upstream (OpenCE 13c14df9): online co-op campaign, online split screen, network version 16
Upstream's online co-op (network_actors.c, network_coop.c) ticks beside the
VR port's VR relay and bots; its message types end at 77, below the VR
port's from 240. The look and move flags the action test reads moved into
player_control_action_test_note: the head's turning goes there too. The
vertex shader's screen-space undo is upstream's on every platform now, with
the multiview eye offset after it on Android. The APK carries upstream's
brokers.txt beside the FDM layer stub; the updaters keep the VR port's own
repository (upstream's now points at OpenCommunityEdition/OpenCE).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 16:22:05 +02:00
Stefan Maier 5316a6b145 Merge branch 'frame/launcher' 2026-10-05 16:19:02 +02:00
Stefan MaierandClaude Opus 5.5 59b1fb422b Steam Frame: a library button that starts, updates and quits the game
The launcher gives Lepton what Steam gives an Android title of its own (a
compat data path, which the app's data links to after an install, and the
shader path), keeps a container id of its own, tries a start that ends at
once again (a new APK's first start only installs it), and stops when the
game was quit. Steam's "Exit game" cannot reach the game in Lepton's
container: the VR options get "Quit Halo" (asked twice), which on Android
now ends the process and leaves quit_requested.txt for the launcher. The
library's pictures are in port/android/steam_frame_grid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 16:17:35 +02:00
Stefan MaierandClaude Opus 5.5 5ecd3075a0 Steam Frame: a launcher that starts the game as Steam starts Android titles
Lepton runs the APK in a container of its own, kept between starts, and
installs it anew whenever the APK is newer; the script starts it a second
time after a new APK's first start, which only installs it. With the APK in
~/Documents/HaloCE, updating the game is replacing that file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 15:17:54 +02:00
MrBruh 13c14df95c Merge pull request #101 from xshxdex98/coop-bsp-sync
Co-op: every machine stays on the host's BSP (network version 16)
2026-10-06 02:13:10 +13:00
xshxdex98 b525bf8514 Network version 16
A client's input says which structure BSP it has loaded (co-op).
2026-10-05 08:09:47 -05:00
xshxdex98 087ca8a875 Co-op: every machine stays on the host's BSP
Players in big sessions ended up on different BSPs:

- The host told clients about a switch only every half second, and not
  reliably, so a client could predict its player for a while in a BSP it
  hadn't loaded. A switch is now sent at once, reliably, ahead of the
  players' new positions.
- A client still loading reports so: its input carries the BSP it has
  loaded. Until it has the host's, the host takes none of its movement
  (which falls there with no floor) and none of its loading zones.
- After a switch, by a trigger, a script or a skipped cutscene, no trigger
  can switch again until every machine has loaded the new BSP (at most
  ten seconds), so no client falls two switches behind.
- A player the host can't place beside whoever crossed, when a big team
  runs out of room there, is placed beside another teammate instead of
  being left where they were.
2026-10-05 08:09:46 -05:00
MrBruh 24957ff6a0 Network version 15 2026-10-06 01:28:04 +13:00
MrBruh 402d4b8351 Merge pull request #97 from xshxdex98/coop-dead-vehicles 2026-10-06 01:24:23 +13:00
MrBruh d1825ab08d Merge pull request #96 from xshxdex98/coop-backtrack-gate 2026-10-06 01:24:22 +13:00
MrBruh 268ec84b74 Merge pull request #95 from xshxdex98/coop-allegiances 2026-10-06 01:24:21 +13:00
xshxdex98 8dc796028e Co-op: going back to a visited BSP needs two thirds of the team
One player who stayed behind or went AFK no longer blocks the team.
2026-10-05 07:14:28 -05:00
xshxdex98 8aa381ed14 Co-op: vehicles the host destroys explode on clients
Clients only had a kill path for bipeds, so a Ghost, Banshee or Wraith the
host destroyed stayed intact on clients. A vehicle the host's state says
is dead is now destroyed there too, with its explosion.
2026-10-05 07:01:37 -05:00
xshxdex98 c3f82fa9b2 Co-op: going back to a BSP already visited needs the whole team there
A loading zone brings the whole team to whoever crosses it. Into a new part
of the level that's what you want, but it let one player drag everyone
back. A switch back to a BSP the team has been in now waits until every
living player is in the trigger or near the player in it.
2026-10-05 07:01:32 -05:00
xshxdex98 5ff4b6ee8d Co-op: clients take the host's allegiances
The scripts make allegiances (ai_allegiance player human) and only the host
runs them, so on clients marines stayed enemies of the players: red reticles
and the wrong team. The host sends its ally and friend tables with the
presentation each tick.
2026-10-05 07:01:26 -05:00
MrBruh 91e838e2f6 Network version 14 2026-10-06 00:54:11 +13:00
MrBruh 520b8401db Merge pull request #93 from xshxdex98/test-death-timing-coop 2026-10-06 00:51:37 +13:00
MrBruh 2ed4a7b49d Merge pull request #92 from xshxdex98/coop-flood-dropships 2026-10-06 00:51:36 +13:00
MrBruh 503992d544 Merge pull request #91 from xshxdex98/coop-device-states 2026-10-06 00:51:35 +13:00
MrBruh ccc7df6611 Merge pull request #90 from xshxdex98/coop-loading-zones 2026-10-06 00:51:35 +13:00
xshxdex98 ddcbe79283 test_death_timing.py: stubs for the network co-op paths
The test compiles main.c's and players.c's death handlers against a fake
world; their network co-op branches call functions it had none of, so it
no longer compiled. Stubbed off, it covers the local game it was written
for, and passes.
2026-10-05 06:41:27 -05:00
xshxdex98 862d88737e Co-op: Flood bodies get up on clients; dropship doors open and close there
- A body the host's unit got back up from (a Flood combat form feigning
  death) gets up on the clients too, instead of its ragdoll being dragged
  about. The resurrection is one function both use (unit_port_resurrect).
- The scripts' unit_open and unit_close go to the clients, so dropships'
  doors aren't left open there.
2026-10-05 06:36:27 -05:00
xshxdex98 8610b28376 Co-op: doors and elevators are where the host has them
The host sends each device's position and power when they change (and with
the resent state), and a client puts its device there when the host's is at
rest and they differ, or when they drift apart while it moves. Clients only
had the device groups' values, which aim a device: a door could stop half
open, and an elevator be somewhere else on a client, so its players fell.
2026-10-05 06:36:27 -05:00
xshxdex98 9e6247e0cb Co-op: a loading zone brings the team along; only someone falling is rescued
- Crossing a BSP loading zone moves every other player to whoever crossed,
  as split screen does. Moving only some players left the rest where the
  next switch threw them back to a hallway, or under a floor.
- The rescue for a player outside the loaded BSP waits two seconds and
  only takes someone falling: a player riding an elevator through its
  shaft is left alone.
2026-10-05 06:34:00 -05:00
MrBruh dea5fe3b1e Fix crash past 256 actors 2026-10-06 00:29:42 +13:00
MrBruh f0804aa587 Extra enemies: per player or static multiplier 2026-10-06 00:08:26 +13:00
MrBruh 16ad4cc900 25% extra enemies option 2026-10-05 23:45:31 +13:00
MrBruh a38ede078a Co-op extra enemies per player 2026-10-05 23:25:15 +13:00
MrBruh fa85aa53f5 Kick command; co-op friendly fire on by default 2026-10-05 22:51:05 +13:00
MrBruh 8be9a038a1 Co-op friendly fire setting 2026-10-05 22:40:34 +13:00
MrBruh 783b591a6a Co-op defaults to 16 players and private 2026-10-05 22:27:36 +13:00
MrBruh e82d880060 Singleplayer and multiplayer map choice 2026-10-05 22:17:22 +13:00
MrBruh 7d0e587c51 Co-op review fixes 2026-10-05 21:02:04 +13:00
MrBruh 01586cfebf Merge pull request #89 from xshxdex98/coop-campaign 2026-10-05 20:29:19 +13:00
MrBruh de52060644 Host takes grenade damage only from grenades thrown 2026-10-05 19:54:33 +13:00
MrBruh ab7c507a29 Leaving the lobby after a game goes to the main menu 2026-10-05 19:39:57 +13:00
xshxdex98 ba6fe0a826 AI sync: any team the host's unit has goes with it, not only the campaign's 2026-10-05 00:59:33 -05:00
MrBruh 3f6c0c37a4 Friendly fire stops teammates' instant kills 2026-10-05 18:47:39 +13:00
xshxdex98 606109551d Co-op: friendly reticles, skipped cutscenes' sound, the end of a level, tutorial prompts
- AI units' teams go with their states, so friendlies aren't red on
  clients' reticles.
- A skipped cutscene's music and dialogue stop on the clients.
- Winning a level returns every machine to the lobby with the next level
  set up, instead of leaving them on its last screen.
- Help text carries whether the host shows it, so no prompt is dropped.
2026-10-05 00:46:51 -05:00
xshxdex98 8194c5a00b Online co-op: play the campaign together over LAN and the internet
Hosting a co-op level from the Map screen (CO-OP CAMPAIGN, a level and its
difficulty, then Server Setup) makes a network game on the campaign map
with no game engine, which is what co-op is. Only the host runs the
scripts and the AI; port/linux/game/network_coop.c sends clients what the
scripts do (cutscenes, camera, fades, HUD, sounds, devices, named objects,
animations), and network_actors.c drives the host's AI units on every
machine. Players spawn beside each other, respawn beside a safe teammate,
and spectate while dead or waiting. Skipping a cutscene is a vote.
Players can join a game in progress at any time.
2026-10-05 00:31:30 -05:00
MrBruh 737551fd51 Resolution, scaling and window size settings 2026-10-05 18:28:58 +13:00
xshxdex98 7a56c624f5 Merge upstream main 2026-10-05 00:07:13 -05:00
MrBruh 3ae09c3d2c Fade the radar wedge out fully 2026-10-05 17:23:47 +13:00
MrBruh 3a22816c2f Merge pull request #81 from FDervish/fix/antenna-stepped-per-tick 2026-10-05 17:15:38 +13:00
MrBruh 2853a42253 Clear an antenna's shape record when it is created 2026-10-05 17:13:27 +13:00
Fadi DerouicheandClaude Opus 5 39ece781ee Step the antenna chain in the tick and draw the frames between the ticks
An antenna's chain is a simulation of the Xbox's own step, an update a tick,
and the port was stepping it once a frame with the frame's own time. Its
springs, its carry and the points' physics then acted against the frames'
movement of it, so how far the chain moved per update depended on the frame
rate, and a vehicle at speed swung it far wider than a tick's step does.

- game.c steps the chain in the tick, beside the effects, which are already
  stepped there.
- antenna.c records the shape each tick leaves, relative to the point the
  vehicle's marker carries the base by. A frame drawn between two ticks is the
  part of the way between those two shapes that it is, carried to where the
  marker is now, so the chain stays on the vehicle it hangs from instead of
  lagging a tick behind it. The gate that left a chain that had not been drawn
  for five updates unstepped counted frames; it counts ticks now, which is
  what it counted on the Xbox.
- widgets.c no longer steps the chain from the widget update, which ran once a
  frame.
- game_time.c draws the per-frame effects and widgets that hang off objects
  (game_frame) with the pose between the last two ticks, the pose the renderer
  draws with, so what they place on an object is drawn where they put it.
- objects.c hands markers the matrices the renderer draws with, so an effect,
  a particle system or a contrail attached to a moving object follows it
  between the ticks too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 17:13:27 +13:00
MrBruh cf17201ec8 Split screen divider spans wide screens 2026-10-05 17:13:27 +13:00
MrBruh 45de17116b Merge pull request #82 from addisonbair/gamescope-fullscreen
Count a fullscreen window that gamescope never confirms as fullscreen
2026-10-05 17:01:51 +13:00
xshxdex98 7d56f34b83 Map screen: choose co-op campaign or multiplayer, then vanilla or custom maps
Hosting over LAN or the internet, the Map screen's list first offers CO-OP
CAMPAIGN or MULTIPLAYER. CO-OP CAMPAIGN offers CAMPAIGN or CUSTOM CAMPAIGN,
then a level and its difficulty. MULTIPLAYER offers VANILLA MAPS or CUSTOM
MAPS, then the maps, and goes on to the gametypes as before. B steps back
through the choices and leaves the screen from the first. Split screen
starts at VANILLA MAPS / CUSTOM MAPS.

The custom lists stay empty and a co-op level cannot be hosted yet: network
co-op and Custom Edition maps come in their own pull requests, which fill
them in.
2026-10-04 22:54:24 -05:00
MrBruh 3c7ba95ff5 Merge pull request #70 from startupfoundry/windows-no-console
Release builds of Windows without a console window
2026-10-05 16:51:22 +13:00
MrBruh 092505224a Merge pull request #25 from garfbargle/macos-android-build
Build the Android port on macOS hosts
2026-10-05 16:48:39 +13:00
MrBruh 079883d640 Merge pull request #59 from saulob/fix/startup-color-flash
Fix incorrect Colors on the initial menu frame
2026-10-05 16:39:03 +13:00
MrBruh 54faa1dab6 Merge pull request #87 from eltanschauung/fix/death-camera-timing-pr
Fix death camera and co-op respawn timing at high frame rates
2026-10-05 16:33:14 +13:00
MrBruh 273152cc25 Merge pull request #80 from FDervish/fix/vertex-explosion
Fix vertices exploding to the screen centre near the camera plane
2026-10-05 16:07:31 +13:00
MrBruh 9bdd7f30c0 Merge pull request #86 from thelinkin3000/fix/host-again-after-backing-out
Make a new game each time Create is chosen
2026-10-05 15:53:59 +13:00
MrBruh 685bf260a0 Either co-op player picks the level 2026-10-05 14:47:11 +13:00
MrBruh 933ed2b7b8 Point updates at OpenCommunityEdition/OpenCE 2026-10-05 14:21:03 +13:00
MrBruh c115f5292d Split screen joins of games under way 2026-10-05 14:06:51 +13:00
eltanschauung 0f22c1d592 Add standalone death and co-op regression tests 2026-10-04 20:33:17 -04:00
eltanschauung fb1abedd8b Keep dead camera target when no living player exists 2026-10-04 20:32:59 -04:00
eltanschauung cd345aac7c Count death and co-op respawn delays in game ticks 2026-10-04 20:32:31 -04:00
MrBruh 7e00135d31 Fix menu crash with no network client 2026-10-05 12:59:15 +13:00
thelinkin3000andClaude Opus 5.5 95416b2cfc Make a new game each time Create is chosen
Backing out of a hosted game keeps its server: the lobby's last local
player leaving pauses it (netgame_unjoin_player). Choosing Create again
threw the host's client away, but network_game_start_new_server makes and
joins a server only when there is none, so the new client never joined the
one left over. The lobby then had nobody in it: START added no player, B
could not leave (not in pregame), and the server's map changes failed to
reach the client that was gone.

Create now disposes the client and server first, so it always makes a new
game and joins it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:37:36 +00:00
MrBruh ffc1512dcb Read brokers.txt without SDL on Android 2026-10-05 11:27:05 +13:00
MrBruh 88a7c07ec7 Brokers in brokers.txt 2026-10-05 11:15:11 +13:00
Stefan Maier 8e8713681d Merge remote-tracking branch 'origin/fix/multiview-desktop-compile' 2026-10-04 22:59:55 +02:00
Stefan Maier d7099f3e40 Fix desktop link: bot machine check and glClearDepthf were Android-only
network_game_server_is_bot_machine (cea7abfc, Bots as players of the
host) was written into the HALO_ANDROID block of network_server_manager.c
while bots.c calls it on every platform, and so was the array it reads;
both move out of the guard (on the desktop nothing marks a bot machine,
so the check is always false there, the writes stay Android-only).

glClearDepthf (dc21f4b4, Play in VR on OpenXR headsets) is ES-only:
GL_FUNCTIONS declares it for Android's loader and the define at the top
maps glClearDepth to it, but the desktop context links against GL's
glClearDepth. The clear now calls glClearDepth, which the Android define
turns into glClearDepthf as before.
2026-10-04 22:37:17 +02:00
Stefan Maier 4ba36d7305 VR: fix desktop build, targets_multiview was Android-only
6c3f4ce2 (VR: draw both eyes in one pass) changed prepare_draw to pass
targets_multiview to vertex_shader_get, but the variable is declared and
kept inside the HALO_ANDROID guard (L887): Linux and Windows failed to
compile with 'use of undeclared identifier targets_multiview', Android
was fine. On the desktop there are no multiview layers to stand in for
the back buffer, so the desktop call passes FALSE.
2026-10-04 22:25:21 +02:00
MrBruh e3389991ac Online split screen 2026-10-05 09:18:40 +13:00
Stefan Maier 855f083117 Merge branch 'frame/performance' 2026-10-04 22:04:33 +02:00
Stefan Maier 6f3786de91 Merge branch 'fix/vr-shot-origin' 2026-10-04 22:04:33 +02:00
Stefan MaierandClaude Opus 5.5 493713b0a6 Steam Frame: the refresh rates offered in the log; its rate and speed noted
The runtime offers only the rate SteamOS is set to. At 72 Hz the game holds
it with multiview (about 11.6 ms a frame); at 90 Hz it cannot, and the
frames SteamVR makes up showed as a doubled picture when the head turned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:47:56 +02:00
Stefan Maier bf356fc58a Merge branch 'fix/vr-shot-origin' into frame/controllers 2026-10-04 21:42:21 +02:00
Stefan MaierandClaude Opus 5.5 6a7b919522 VR: shots fly along the aim, not above it
A shot fired from the muzzle of the weapon in the hand was kept inside the
level by the game's test of the line from the eyes to it, which in VR runs
down through the player's own body: the test hit it and moved the shot up
towards the eyes, from where it flew parallel to the aim, a few centimetres
above the crosshair and the laser (measured: up to 7 cm at 3 m). In VR the
line is tested against the level alone (a hand through a wall still does
not fire through it); measured after: the impacts on their rays, on
average within a millimetre.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:42:07 +02:00
Addison Bair 20861c06d2 Count a fullscreen window that gamescope never confirms as fullscreen
gamescope (the Steam Deck's Game Mode) makes the window the size of the
display but never confirms the fullscreen request, so SDL leaves
SDL_WINDOW_FULLSCREEN unset. platform_screen_mode then reported a window,
the game drew the Xbox's 640x480, and gamescope stretched it to the
screen, soft and 4:3. A window that was asked to be fullscreen and covers
its display now counts as fullscreen too, so the game draws at the
display's resolution and shape (1280x800 on the Deck).
2026-10-04 19:30:07 +00:00
Fadi DerouicheandClaude Opus 5 fc7271fd52 Step the antenna chain in the tick and draw the frames between the ticks
An antenna's chain is a simulation of the Xbox's own step, an update a tick,
and the port was stepping it once a frame with the frame's own time. Its
springs, its carry and the points' physics then acted against the frames'
movement of it, so how far the chain moved per update depended on the frame
rate, and a vehicle at speed swung it far wider than a tick's step does.

- game.c steps the chain in the tick, beside the effects, which are already
  stepped there.
- antenna.c records the shape each tick leaves, relative to the point the
  vehicle's marker carries the base by. A frame drawn between two ticks is the
  part of the way between those two shapes that it is, carried to where the
  marker is now, so the chain stays on the vehicle it hangs from instead of
  lagging a tick behind it. The gate that left a chain that had not been drawn
  for five updates unstepped counted frames; it counts ticks now, which is
  what it counted on the Xbox.
- widgets.c no longer steps the chain from the widget update, which ran once a
  frame.
- game_time.c draws the per-frame effects and widgets that hang off objects
  (game_frame) with the pose between the last two ticks, the pose the renderer
  draws with, so what they place on an object is drawn where they put it.
- objects.c hands markers the matrices the renderer draws with, so an effect,
  a particle system or a contrail attached to a moving object follows it
  between the ticks too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-04 19:45:28 +01:00
Stefan MaierandClaude Opus 5.5 c1305fa94f Steam Frame: its controllers as themselves, with the Xbox's layout
SteamVR offers the Frame's controllers by their own profile
(XR_VALVE_frame_controller_interaction); without bindings for it, it mapped
the Quest's onto them, and X and Y landed on the left one's D-pad. Now: the
right one's A, B, X, Y and menu button are the game's A, B, X, Y and Start
(the Xbox's layout), the left one's View is Back and its D-pad the D-pad,
the bumpers switch grenades and the flashlight. The VR options' page of
buttons shows the Frame's layout there. The runtime's extensions and the
controllers' profile are in the log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:28:13 +02:00
Fadi DerouicheandClaude Opus 5 3d2c04d675 Fix vertices exploding to the screen centre near the camera plane
The screen-space conversion that the Xbox's vertex shaders do is undone by
multiplying the screen position by w again, which is lossy near the camera
plane: the conversion takes the reciprocal of w (rcc), which clamps there, and
1/w rounds differently on each GPU.

Worse, where w is zero or not a number the reconstruction leaves the vertex at
the clip-space origin. That point is inside the frustum, so nothing clips the
triangle away, and OpenGL's zero divided by zero puts the vertex in the middle
of the screen: the triangle is then drawn from wherever its other vertices are
out to there. On the first-person weapon, whose pose follows the camera and so
reaches the camera plane, that is a streak across the whole view. The Xbox's
own divide sends such a vertex to infinity and its clipper discards the
triangle.

Compute the final position from the clip-space position that the shader keeps
whenever it takes the reciprocal of w, so that nothing is reconstructed by
dividing; and where w is zero or not a number, put the vertex behind the
camera (w = -1), which the clipper also discards, as the Xbox did.

Both paths were behind the Android build's #ifdef, so desktop GL only ever
used the lossy reconstruction. The y flip and depth range that follow remain
per platform.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-04 19:27:35 +01:00
Stefan MaierandClaude Opus 5.5 0fc85d6c67 Steam Frame: the overlays' alpha from their colour on SteamVR
SteamVR draws a quad's texels by their alpha alone, and the game's menus,
HUD and text have none in their additive parts: on the Frame nothing showed.
On SteamVR (the runtime's name says) the copy into the quads' images takes
their alpha from their colour where it is higher; HALO_XR_QUAD_ALPHA
(colour, keep) overrides, for trying. The Frame's notes updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:17:43 +02:00
Stefan MaierandClaude Opus 5.5 c0a08f395e Steam Frame: the game's data in the SteamOS user's Documents when it is there
Lepton empties the app's own folders (and uninstalls the app) whenever it
starts again; Documents is the SteamOS user's and kept. When
/home/steamos/Documents/HaloCE holds the maps and is writable, the launcher
and the game take it as the data folder (maps, config.toml, saves, logs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 19:51:08 +02:00
Stefan MaierandClaude Opus 5.5 06cb70fb58 Steam Frame: notes, and no variable the Android loader never reads
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 18:27:28 +02:00
Stefan MaierandClaude Opus 5.5 1709698053 Start on the Steam Frame (Lepton)
Lepton has no clipboard service, and SDL3's activity died taking one for
granted: the build now patches SDL's clipboard handler to do without.
The Frame lists an implicit OpenXR layer for Vulkan apps whose library an
app cannot load, which failed xrCreateInstance (and the Android loader
reads no variable to turn it off): the app now carries a pass-through
layer under that library's name. With both, the game runs on the Frame
and its OpenXR session starts (SteamVR, 1728x1728 an eye, 72 Hz).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 18:26:39 +02:00
Stefan Maier cb72dca40f Merge branch 'merge/upstream-2026-10-04' 2026-10-04 14:00:26 +02:00
Stefan MaierandClaude Opus 5.5 bbea2a2bff The online panel's game by its row past the fixed rows
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:47:56 +02:00
Stefan MaierandClaude Opus 5.5 57cdbd8f80 Merge upstream (cybersecurity/halo-ce-universal 193cbf59): server browser, split-screen co-op
Upstream's server browser of public internet games (p2p_lobby.c) and the VR
port's own public list share the brokers: its topics are a table now, and
the VR port's (join codes' and public games') are entries of it; the VR
port's retained publishes carry MQTT 5's empty properties. The VR port's
public toggle lists the game in the server browser too, and the VR online
panel shows the server browser's games (PC, phones, VR) first, joined by
their invites, then its own list's that it has not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:47:29 +02:00
Stefan Maier c5c5a8eaeb Merge branch 'feature/bots-vehicles' 2026-10-04 13:33:07 +02:00
Stefan Maier a3ee36b5ef Merge branch 'feature/vr-hud-fixes' 2026-10-04 13:33:07 +02:00
Stefan Maier 30a1e0f40e Merge branch 'feature/vr-menu-lists' 2026-10-04 13:33:07 +02:00
Stefan Maier 327cf96d6f Merge branch 'feature/vr-options' 2026-10-04 13:33:07 +02:00
Stefan MaierandClaude Opus 5.5 867855d388 Bot drivers see walls and rocks ahead, back off when stuck, go round teammates
Driving a jeep or a scout, a bot looks ahead (farther the faster) along the
way it turns: a wall or a rock there (not ground it can drive up) is passed
on the most open side nearest its way, slower, and boxed in it backs off.
Stuck is now making no way along its heading for a second and a half, which
spinning wheels no longer hide. A teammate in the way is gone round slowly
on the other side instead of waited for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:25:15 +02:00
Stefan MaierandClaude Opus 5.5 4af570bc08 VR: the online games' hint further left and shorter, clear of Y = Create Game
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:13:27 +02:00
Stefan MaierandClaude Opus 5.5 7a1cf79396 VR: teammates' triangles over their heads, a click joins on the profile screen
The triangle the game draws over each teammate goes into the name's slot
over the head (shown in the world there, not on the HUD before the eyes,
where it hung distorted), under the name, or alone when names are off. On
the screen where controllers join by pressing A, a click is the pad's A. On
the screen of games to join, the online games' hint goes into the key bar's
free left and the join status under it, clear of the map's picture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:00:41 +02:00
Stefan MaierandClaude Opus 5.5 9e7fd75f57 Bots: a vehicle left stuck is left alone a while; every third bot follows a person
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 11:41:40 +02:00
Stefan MaierandClaude Opus 5.5 6754403d68 VR: the menu pointer leaves the stick's choice be; clicks beside a list step it
A choice the stick moved to (a map among the tiles) is no longer taken back
by the pointer resting on another, until the pointer moves well away; and a
click beside a list's slots or a value's box, along the way it steps (where
its arrows are drawn), steps it back or on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 11:41:09 +02:00
Stefan MaierandClaude Opus 5.5 aedf5ff9bc Bot drivers wait for those coming to get in, and brake for teammates
Just in, a bot driver waits up to six seconds while a bot goes for its
vehicle or a person of its team stands near it with a gun's seat free; it
brakes (backing, if fast) for a teammate on foot in its way. A bot takes a
vehicle only when the nearest enemy (or the objective) is a long way off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 11:39:24 +02:00
MrBruh 193cbf59c7 Split-screen co-op 2026-10-04 22:27:13 +13:00
Stefan MaierandClaude Opus 5.5 fe160e9dbe Bots man turrets while an enemy is about
A turret near a bot (15 world units) is manned while an enemy is within 35
of it, and left once there has been no one to shoot for ten seconds; at it
the bot aims and fires as at a vehicle's gun. The map's vehicles are named
in the log once a game.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 11:23:49 +02:00
MrBruhandClaude Opus 5.5 d578f88bd0 Validate server browser names as player names
The server browser cleans each game's name as a host cleans the names
of the players and machines that join it, and leaves out a game whose
name has nothing left that names it. A host lists a Latin letter with
a mark as its plain letter, as player names are read.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 20:53:00 +13:00
MrBruhandClaude Opus 5.5 d3fc997fdc Ready walked-over weapons, swap out the selected one
In multiplayer a second weapon is picked up into the empty slot on
walking over it and readied, as in campaign; a client readies the
weapon the host says it picked up once it has it. A swap drops the
weapon the player selected, not the one still in hand mid-switch.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 20:47:06 +13:00
MrBruh a227872ccb Merge pull request #73 from thelinkin3000/fix/unarmed-melee-campaign
Leave an unarmed melee alone where the map has no multiplayer weapons
2026-10-04 20:28:01 +13:00
MrBruh 812ffeea33 Outline the focused server browser row 2026-10-04 20:19:12 +13:00
MrBruh c05864b3fc Leave quit players off the scoreboard 2026-10-04 20:06:28 +13:00
thelinkin3000andClaude Opus 5.5 ce77db8404 Leave an unarmed melee alone where the map has no multiplayer weapons
A player with no weapon, whose biped has no blow of its own, borrows the
melee of the globals' first multiplayer weapon. A campaign map's globals
list no multiplayer weapons, and a10 starts the player unarmed, so a melee
in the cryo tube's tutorial asked for element 0 of an empty block and
halted in tag_groups.c. With no multiplayer weapon there is no blow to
borrow, and the melee does what the game's always did there: nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 06:22:42 +00:00
MrBruh 39350fb81f Enemy names to the motion sensor's range and setting 2026-10-04 18:57:11 +13:00
MrBruh 1a15a171ac No unarmed grenades or melee from vehicle seats 2026-10-04 17:54:34 +13:00
MrBruh 2e3841ff7d Left-align the shotgun's ammo meter 2026-10-04 17:47:55 +13:00
MrBruh 46edf53657 Settings and End Game in the pause menu 2026-10-04 17:20:32 +13:00
Paul Hontz cd747c23c5 Log of the port to halo.log without a console
The release build no longer has a console, and the platform layer's log
(platform_log to stderr: invite links, the data root, crash reports)
went only there. When stderr goes nowhere, halo.log next to halo.exe
gets it instead.
2026-10-03 22:43:14 -04:00
Paul Hontz f148dbd6d9 Release builds of Windows without a console window
The release halo.exe was a console-subsystem PE, so Windows and Wine
open an empty console window next to the game. Under Proton in
gamescope the console can take focus and leave a black screen. Nothing
reads the console (the log is debug.txt), so release builds now link as
GUI with mainCRTStartup; debug builds keep the console.
2026-10-03 22:26:20 -04:00
MrBruh c3adcfe5bf Deflate the release zips again 2026-10-04 14:22:11 +13:00
MrBruh 3304965653 Lobby check test past the compiler launcher 2026-10-04 13:43:13 +13:00
MrBruh c04765d7f4 Server browser of public internet games 2026-10-04 13:34:23 +13:00
MrBruh 933aac6175 No map weapons option, grenades with no weapon 2026-10-04 10:44:37 +13:00
MrBruh 809408c663 Survive kills and joins in departed players' slots 2026-10-04 10:12:32 +13:00
MrBruh 601a4c1f50 Unload the menu map before a client's next game 2026-10-04 09:51:47 +13:00
MrBruh 133d6a5dc1 Free quit players' slots 2026-10-04 09:07:47 +13:00
Stefan MaierandClaude Opus 5.5 12c97ffa85 Bots take vehicles: drive, man the gun, ride along; and race
A bot whose way is long takes an empty vehicle it can drive near it (a
Warthog, a Ghost, a Scorpion), walking to the driver's door and standing
there until the game offers the seat. Driving, it follows a vehicle's path
(from the vehicle's surface, wider, its steps further apart), turning a
Warthog or a Ghost by facing where it goes and pushing a Scorpion there from
where its gun faces; it runs an enemy down, circles one with a gunner
aboard, fires a Ghost's and a Scorpion's guns, backs off what it is stuck
on, roams between the map's spawn points, and gets out at the objective,
overturned, stuck again and again, or with an enemy close while it stands.
In a game of teams a bot mans the gun of a teammate's vehicle, a person's
first (or rides along with a person), aims and fires it, and gets out once
no one drives. Overturned vehicles near it are turned upright.

In a race a bot heads for the flag it is to touch next
(game_engine_race_next_flag), in a vehicle if it can have one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 16:48:59 +02:00
saulo benigno 61623e68d5 Fix texture bindings after first upload 2026-10-03 11:41:54 -03:00
MrBruh 23b542601f Unarmed melee and infinite grenades for any player count 2026-10-04 03:22:16 +13:00
MrBruh 62b630a261 Remove the flamethrower and gravity rifle 2026-10-04 03:05:59 +13:00
Stefan MaierandClaude Opus 5.5 0e6327cadf The controls page lists switching grenades (B held) on a line of its own
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:41:56 +02:00
Stefan MaierandClaude Opus 5.5 5f82df71f0 VR options: Y on the main menu or the pause menu
A panel of the VR settings one may change while playing (turning, snap
angle, smooth speed, the vehicles' comfort vignette, the Warthog's wheel,
the weapon laser, the crosshair, the motion tracker on the wrist), written
to config.toml, which vr.c reads again as they change; and a page of what
the controllers' buttons do, on foot, in vehicles and in the menus. The
pointer works both, and its hint opens the panel. The menus' screens are
named in the log as they come up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:37:10 +02:00
Stefan Maier cfdd821b45 Merge branch 'merge/upstream-v11' 2026-10-03 15:21:57 +02:00
Stefan MaierandClaude Opus 5.5 a9bbce5f7a Merge upstream (cybersecurity/halo-ce-universal c9ee319a): PC menus, network version 11
Upstream's PC menus are kept for the desktop; in VR the Xbox's stay, which
the VR port's join codes, game list, host options and keyboard pointer are
built into. config_write_integer now writes through upstream's config_write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:11:07 +02:00
MrBruh e8e0c2215a Find the port's headers in Windows game units 2026-10-04 02:08:45 +13:00
MrBruh 383355381c Kills to win up to 500 in the PC menus 2026-10-04 02:04:14 +13:00
Stefan Maier e94906100c Merge branch 'feature/warthog-wheel' 2026-10-03 14:58:32 +02:00
Stefan Maier 57add5ed71 Merge branch 'feature/vr-menu-pointer' 2026-10-03 14:58:32 +02:00
Milenko 2b0103a0ef Slayer's kills to win up to 500, for big games
The game type editor's kills to win (Slayer and Team Slayer) went up to
50, which a game of many players reaches in moments: it now also offers 75,
100, 150, 200, 250 and 500. The spinner's items are its string list's
(ui.map's 5, 10, 15, 25, 50) and then strings of the port's own, each with
its description; the options list's descriptions count only a spinner's
items of the tag's own, so those of the rows after it are where they were.
A spinner with extra items is a little wider, for three digits.
2026-10-04 01:58:26 +13:00
MrBruh c9ee319ab5 Add the PC menus 2026-10-04 01:56:45 +13:00
Stefan MaierandClaude Opus 5.5 770f8881e0 At the wheel, the right trigger is the throttle and the left the brake
Holding a Warthog's wheel, the triggers drive as the left stick pushed up
or down would (backwards once stopped), unless the stick is pushed further;
they neither fire nor hold a grenade then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:50:41 +02:00
Stefan MaierandClaude Opus 5.5 38ab192749 Steer a Warthog by its wheel with the hands
At an open vehicle's wheel, a hand that grips near it (where the driver's
drawn hands hold it) takes hold, and the hands turn it about its column:
both by the line between them, one about the wheel's middle. Turned
vr.vehicle_wheel_degrees (90) either way it steers fully; let go, the right
stick steers as before. Holding the wheel, the right grip is no melee.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:00:26 +02:00
Stefan MaierandClaude Opus 5.5 827eb0fc1d The keyboard gets the menu pointer's clicks; its BACK and ENTER hints click
The menus' mouse handling took the pointer's clicks each frame before the
keyboard asked for them, so the keyboard never saw one: with the keyboard
up the pointer is now the keyboard's alone. Clicking the hints under it
(B = BACK, A = ENTER, in its picture) cancels or finishes as Done does.
The pointer counts as moved past 3 pixels, not a hand's tremor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:47:51 +02:00
Stefan MaierandClaude Opus 5.5 a482a5f5bb The on-screen keyboard follows the menu pointer in VR
The key under the controller's pointer is picked and a click types it.
In the menus the triggers no longer reach the game as the gamepad's (they
click the pointer; on the keyboard they also moved its cursor).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:42:57 +02:00
Stefan MaierandClaude Opus 5.5 81f321f959 Of overlapping screen keys, the one whose icon is nearest on the left
A key's label is a text box often far wider than its text, reaching over
the next key (Back pressed Delete). Of the keys under the pointer, the one
whose icon begins nearest on its left is taken: each key from its icon to
the next one's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:30:20 +02:00
Stefan MaierandClaude Opus 5.5 f97ff9219f Menu pointer: narrower keys, the click where the trigger began to move
The screen keys' margin in VR was wide enough to reach the next key (Back
pressed Delete): now 2 pixels to the sides and 4 above and below, with a
label at most 25 pixels off. A click is taken where the controller
pointed as its trigger began to move (past a tenth), not a fixed time
before, which could still be on the key it just left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:27:28 +02:00
Stefan MaierandClaude Opus 5.5 85e79a067a Steadier menu pointer clicks in VR
A click is taken where the controller pointed a few frames before the
trigger went down (pulling it tips the controller), and in VR a screen
key's button takes a label up to 40 pixels off and a margin of 6 about it,
so the keys under the menus (A, B, Back) are hit as readily as items.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:23:28 +02:00
Stefan MaierandClaude Opus 5.5 effeda1b7c Point at the menus with a controller in VR
The desktop builds' mouse in the menus (halo_ui_pointer.h) now comes, on
Android in VR, from a controller: where its aim's line meets the menus'
screen fixed in the room is the pointer, the trigger clicks (the hand
pressed last points; a click ticks its rumble), and a ringed dot shows
where it points. The item under it takes the focus and a click presses A,
as with the mouse; the online games and host options panels take it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 11:56:05 +02:00
Stefan Maier 6299503191 Merge branch 'feature/bots-team' 2026-10-03 11:10:25 +02:00
Stefan MaierandClaude Opus 5.5 c5d86c8d8b Bots spread out about their objective and keep to it
Each bot has a direction of its own (the golden angle on from the last):
far from a shared goal (the enemy's flag, the ball, the hill) it comes by
a way of its own, out to its side of the straight way, straight only for
the last 10 world units; holding a place it takes a spot of its own: on
the hill, about its flag's stand (every other guard ahead of it towards
the enemy's), about a teammate it escorts. With no way to its spot it
takes the point itself. A bot with an objective no longer leaves it in a
fight for a weapon close by.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:55:28 +02:00
Stefan MaierandClaude Opus 5.5 7242151017 Names over the heads at 60% of their size on the HUD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:48:01 +02:00
Stefan MaierandClaude Opus 5.5 1c8e651bb1 Bots play with their team
In a game of teams a bot holds its fire while a teammate stands in the
line of it (and steps aside for a clear shot), takes on first the enemies
near its teammates (people above all), escorts a teammate carrying the
enemy's flag or the ball a few world units off on its own side, and, with
nothing to do, every other bot keeps near the nearest person on its team.
A goal it finds no way to three times running (a person up where only a
teleporter goes, a thing out of reach) is given up for a while, and a
roaming bot then goes somewhere near at random.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:41:08 +02:00
Stefan MaierandClaude Opus 5.5 4b8f8dde8b Players' names over their heads in VR, each on a quad of its own
The HUD in VR is a screen at one distance before the eyes, where the names
upstream draws on it hung. Each name is now drawn into a slot of a region
of the back buffer before the HUD (up to eight, one under another), taken
into a names layer, and shown on a quad of its own over its player's head,
facing the eyes, as wide as it would look on the HUD: in depth, for both
eyes. Who is named, and when, is as upstream has it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:31:36 +02:00
Stefan Maier 920b1bfea9 Merge branch 'feature/vr-scope' 2026-10-03 10:18:30 +02:00
Stefan Maier 09f869e44c Merge branch 'feature/bots-modes' 2026-10-03 10:18:30 +02:00
Stefan MaierandClaude Opus 5.5 bc45338214 Bots get stuck in corners less
A path the game finds but cannot steer round the objects on it (the other
players among them: obstacle avoidance failed) is walked as smoothed,
instead of none (which had bots walk straight into walls); a step aside
settles a bump. A bot goes on to the next point of its path when it is
in plain view on its own level, walks the last 3 world units straight
only when nothing is in the way, and counts itself stuck when it makes no
way to its point for 6 ticks (only while it walks one): then it steps
aside, finds its way anew, skips a corner's point it cannot reach, jumps
at 12 and goes elsewhere later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:33:58 +02:00
Stefan MaierandClaude Opus 5.5 a663d1b100 Bots take good weapons in a fight too, and value strong ones more
Within 10 world units a bot goes for a weapon or powerup worth at least 4
while it fights. Strong weapons are worth a longer way (worth squared over
distance, falling off over 15 world units), and with two weapons one is
worth what it betters the worse by and 1.5 more, so rocket launchers and
sniper rifles are taken, not only the shotgun close by. The pistol counts
for a little less up close, the assault and plasma rifles more.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:27:24 +02:00
Stefan MaierandClaude Opus 5.5 d8034d37fd Raise the rocket launcher's scope disc 3.5 cm more (18 cm)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:03:19 +02:00
Stefan MaierandClaude Opus 5.5 f8a39df0cd No names over the heads in VR for now
In VR the HUD is a screen at a fixed distance before the eyes, so the
names upstream draws on it hung there instead of over the players' heads.
Off until they are drawn into each eye's picture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:03:10 +02:00
Stefan Maier 6057146244 Merge remote-tracking branch 'upstream/main' into merge/upstream-2026-10-03
# Conflicts:
#	port/linux/game/network_distributed.c
#	port/linux/game/network_distributed.h
2026-10-03 07:38:52 +02:00
Stefan MaierandClaude Opus 5.5 94b17a7913 Bots play capture the flag, king of the hill and oddball (phase 4)
The flags and balls (weapons that are flags) are gathered each tick with
where they are and who carries them. In CTF a bot carrying the enemy's
flag runs it to its own stand; the rest hunt whoever carries their flag,
touch it home where it lies dropped, or go for the enemy's (every third
guards its own). In king of the hill they go to the hill (the game's goal)
and keep moving in it. In oddball they go for the ball, taken as a weapon
is, hunt its carrier, and carrying it keep away. Carrying, a bot does not
fire, and looks where it runs. The last 3 world units to a goal are
walked straight, over what the path goes round (a flag on its stand).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:22:31 +02:00
Stefan MaierandClaude Opus 5.5 30f362ff3d Bots pick up weapons, powerups and grenades, and throw grenades (phase 2)
Out of a fight a bot looks every second for what lies within 25 world
units worth having: a weapon better than the worse of its two (it changes
to that one and holds the swap while the game offers the weapon), ammo for
an empty weapon it carries, an overshield, camouflage, a health pack when
hurt, grenades when it has few. In a fight it takes the weapon that suits
the distance (shotgun near, sniper rifle far, no rocket up close), at
most every two seconds, changes from an empty one, and now and then lobs
a grenade at an enemy 6 to 20 world units off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:41:48 +02:00
Stefan MaierandClaude Opus 5.5 23d14319a7 Raise the rocket launcher's scope disc 3.5 cm more
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 18:14:34 +02:00
Stefan MaierandClaude Opus 5.5 ff5b91f120 Raise the VR scope's disc over the rocket launcher's tube
The disc sits 7 cm over the hand, where a rifle's or pistol's scope is;
over the rocket launcher, carried higher on the shoulder, it was inside
the tube. A weapon may raise it (halo_vr_set_scope_raise): the rocket
launcher by 11 cm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 18:07:26 +02:00
Stefan MaierandClaude Opus 5.5 f6c2322df9 Host options in the lobby: public, bots and their skill
Y in a hosted game's lobby opens a panel to make the game public, choose
how many bots join (none by default, up to 15) and how well they play
(bots.skill: easy, normal, hard; hard is the first bots' play, normal a
little slower to notice, turn and aim). Integer settings can now be
written to config.toml as booleans could.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 18:05:34 +02:00
Stefan MaierandClaude Opus 5.5 39fbaefaac Keep the new settings out of the network table
The settings file is written in the table's order, so settings of another
section between network.* entries wrote [network] twice, and the whole
file was then refused (defaults used). They now follow the network
settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 17:52:14 +02:00
Stefan MaierandClaude Opus 5.5 c6397cb515 Bots find their way with the game's pathfinding (phase 1)
A bot walks to its goal along a path from ai/path.c (the maps' pathfinding
surfaces, as the campaign's actors use them), found again every half
second: in a fight towards its target, weaving; out of sight hunting the
nearest enemy, or roaming near someone; looking where it goes. With no
path it walks straight at the goal, and when stuck picks another.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 17:05:12 +02:00
MrBruh 80d30410c8 Flush the GPU every 3 draws on Mesa Intel 2026-10-03 02:56:11 +13:00
MrBruh d1c7243cb2 Rework the multiplayer scoreboard 2026-10-03 01:16:09 +13:00
MrBruh fd3d62f960 Keep player names bannable 2026-10-02 22:12:16 +13:00
Stefan MaierandClaude Opus 5.5 5ba6c13097 Bots as players of the host (phase 0)
bots.count bots are added in the lobby as players of machines of the
host's own that nothing connects to (four a machine, from the top slot
down): new connections skip their slots, and they count as machines for
the countdown, so a game of the host and bots starts. Every machine sees
them as players; each tick the host thinks for them (bots.c) and takes
the result as their input through update_server_handle_distributed_input.
The first brain finds the nearest visible enemy, turns at a player's rate
with some aim error, walks at it weaving and fires on target, and else
wanders, jumping and turning when stuck. Every multiplayer map has
pathfinding surfaces for the next phase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:08:09 +02:00
Stefan MaierandClaude Opus 5.5 66c0d33e34 List public online games and join them from a panel
A host can make its game public (Y in the lobby, or network.public_game):
it then announces name, map, game type, players and join code every 10 s,
retained, on hcvr/1/g<network version>/<id> through the signalling
brokers, and clears it when it stops. On the system link list, X opens an
online games panel listing the public games heard lately (stale ones
dropped after 45 s) and a row to type a code; A joins by the game's code.
Announcements carry no addresses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 10:00:57 +02:00
MrBruh b438cea479 Cap enemy name range and shrink names 2026-10-02 20:59:36 +13:00
MrBruh 634b4197f9 Limit enemy names to the weapon's red reticle range 2026-10-02 20:36:46 +13:00
MrBruh ab96597321 Fix the Android build 2026-10-02 20:34:00 +13:00
MrBruh f0dfb58c94 Cap the frame rate with vsync off 2026-10-02 20:26:22 +13:00
MrBruh 9f3e8c92de Add high-res postgame carnage report title 2026-10-02 16:24:30 +13:00
MrBruh f2ba71d9af Show player names above heads in multiplayer 2026-10-02 15:24:40 +13:00
MrBruh 0182da8172 Add high-res text and OpenCE titles 2026-10-02 13:39:17 +13:00
Stefan MaierandClaude Opus 5.5 6b77fe428a Show how joining an online game goes on the system link list
p2p_join_state tells the menus whether the host is being reached, was
reached (its game is in the list), did not answer, could not be reached
(both routers too strict: allow UPnP) or was lost; the join screen shows
it under the code's status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:37:25 +02:00
Stefan MaierandClaude Opus 5.5 0badc451cb Join online games with a short code in VR
A hosted game gets an 8-character join code (Crockford base 32) besides
its invite link, shown under the lobby's buttons. On the system link list,
X opens the game's keyboard to type a code in; the code's topic and key
come from a slow PBKDF2 derivation, the host answers an ask on that topic
through the signalling brokers with its invite sealed with the key, and
the joiner joins as with the link. Codes in join_link.txt work too.

Also: the clipboard text last seen is kept from run to run, so a run's
own invite is not joined again at the next start; in menus a held stick
repeats after 350 ms and then every 90 ms, and its threshold is 70%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:32:50 +02:00
Stefan MaierandClaude Opus 5.5 39a6cc9794 Hold a VR player's relayed aim for a second
Where another machine's VR player aims and fires from is kept for 30
ticks after their hands last came (was 10), over networks that deliver
in bursts; a held grenade still lets go after 10, and a throw is taken
within its own 30.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:36:34 +02:00
Stefan MaierandClaude Opus 5.5 35b90f26fc Send VR players' hands to the other machines
A new distributed message (_distributed_message_vr_poses, last in the
list so builds without it drop it) carries a VR player's controller aim,
muzzle offset, held grenade and last hand throw every tick: clients send
their own to the host, the host relays them with its own. Other machines
aim that player's weapon where the controller points, fire from the hand,
hold the grenade while the hand does and throw it as the hand threw it.
Damage stays the shooter's checked report; network version unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 18:58:52 +02:00
MrBruh c55e4e2b9d Widen only flat UI fills on widescreen 2026-10-02 02:21:22 +13:00
bfenty 7cd1724fe5 Widen full-screen UI widget backgrounds to the display width
A widget background bitmap whose bounds cover the whole 640x480 design
space (the pause menu dim, for example) stopped at the edges of the
centered 640 column on widescreen displays, leaving the side bands
undimmed. Widen the bounds and the clip symmetrically to the full
screen width, mirroring the existing fade_to_black special case in
render_ui_widgets().

Tested on a Retroid Pocket Flip2 (854x480 render target).
2026-10-02 02:16:14 +13:00
MrBruh bd8ec3c798 Add high-res sniper ladder and fix meter fringes 2026-10-02 02:09:45 +13:00
MrBruh 4799980e8a Draw the whole HUD in high resolution 2026-10-02 01:24:38 +13:00
MrBruh f8937c6179 Add high-res HUD textures 2026-10-01 22:33:25 +13:00
Stefan MaierandClaude Opus 5.5 02316c7f35 Merge upstream (92 commits) into the VR port
Upstream's own fixes replace two of ours: reclaiming ART's idle large
object space for the guest's fixed ranges (host_memory.c), and the audio
callback's gathering (host_sdl.c). Its consolidated headers take the VR
declarations (model_animation_definitions.h, render/render.h). The
updaters look at this repository's releases: the original's builds, made
without VR, must never replace this one (they share the package name).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:30:05 +02:00
MrBruh bfbac35761 Fix text glyph edge artifacts 2026-10-01 16:57:38 +13:00
MrBruh 8fb1647e18 Fix weapon swap crash and log spam 2026-10-01 16:20:28 +13:00
MrBruh 943abae15a Fix Windows update unpacking 2026-10-01 14:55:29 +13:00
MrBruh cdd0291a3f Add console log verbosity setting 2026-10-01 14:17:47 +13:00
MrBruh 083eef0c42 Validate player and machine names 2026-10-01 13:45:46 +13:00
MrBruh c5fcfbd46a Kick speed hacks and add bans 2026-10-01 13:12:54 +13:00
MrBruh 71ae1f47bc Fix seventh netcode review 2026-10-01 12:03:52 +13:00
MrBruh a6ca914b8a Fix sixth netcode review 2026-10-01 10:08:53 +13:00
MrBruh e233e1c1bb Fix fifth netcode review 2026-10-01 09:12:07 +13:00
MrBruh 671cc9ed91 Fix fourth netcode review 2026-10-01 06:23:16 +13:00
MrBruh 4f3ce00a8e Fix third netcode review 2026-10-01 04:57:51 +13:00
MrBruh 0d548798e5 Fix second netcode review 2026-10-01 03:54:02 +13:00
MrBruh aad8719ffc Fix client flags in the second game 2026-10-01 02:37:43 +13:00
MrBruh e7d1ed3a67 Harden and fix the netcode 2026-10-01 01:22:46 +13:00
MrBruh c68db56144 Fix the mouse aim merge 2026-09-30 22:38:44 +13:00
MrBruh f100438431 Merge Englis666 mouse-aim-fixes 2026-09-30 22:36:59 +13:00
MrBruh 4885431af9 Merge pull request #27 from Englis666/dedicated-gpu
Ask laptops for the dedicated GPU on Windows
2026-09-30 22:16:25 +13:00
MrBruh a3ed50c56d Remove the lockstep netcode 2026-09-30 22:12:05 +13:00
MrBruh 4adc3a87a5 Remove matching tooling and HALO_LINUX ifdefs 2026-09-30 21:49:46 +13:00
MrBruh b47f237dc1 Convert any listed PAL build 2026-09-30 21:07:47 +13:00
MrBruh a05253c181 Lock multiplayer to known map builds 2026-09-30 21:02:34 +13:00
MrBruh 8fce8887d5 Smooth PAL first-person animations 2026-09-30 20:04:36 +13:00
MrBruh 758f07ea5f Retry the Discord post when rate limited 2026-09-30 19:32:03 +13:00
MrBruh ef9b9e9b1f Fix stale player input after the first game 2026-09-30 19:17:50 +13:00
Englis666andCursor 5ac71baf94 Keep the bullets' autoaim for the mouse
Only the view's magnetism is turned off for the mouse: the bullets still bend toward the target, as with a controller, so shots land as they did.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-30 00:19:37 -05:00
MrBruh a17d89c6f1 Merge bnunu/halo-1 main 2026-09-30 18:13:05 +13:00
Eclisse f3b46607e7 Ask laptops for the dedicated GPU on Windows 2026-09-29 23:38:59 -05:00
Englis666 b9f222222b Enhance direct camera configuration for desktop platforms
Add a new configuration setting for 'display.direct_camera' to enable first-person view adjustments based on player aiming. This update clarifies the behavior of the camera during gameplay and ensures that the setting is applicable only to desktop environments. Additionally, minor adjustments were made to the render_interpolation module to accommodate this feature.
2026-09-29 23:38:59 -05:00
Englis666 eb76e6fb4f Add direct camera rendering for first-person view
Implement direct camera rendering logic for first-person perspective in the render_interpolation module. This includes a new function to handle direct camera updates based on player control and cinematic states, ensuring proper camera behavior across different gameplay scenarios. Additional includes and static variables were added to support this functionality.
2026-09-29 23:38:59 -05:00
Englis666andCursor 767613a3cf Turn off aim assist for the mouse
When the mouse moved after the right stick, the controller's magnetism and autoaim leave the player be (input.mouse_aim_assist, off by default). In lockstep games the autoaim stays, since every machine simulates every shot alike.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-29 23:37:43 -05:00
MrBruh c26f2e84f8 Fix dirty disc error when joining a game 2026-09-30 17:24:48 +13:00
MrBruh 6feea5dbaa Only reclaim ART's idle large object space 2026-09-30 16:31:05 +13:00
Brandon Fenty 114e4a7635 Android: reclaim idle ART reservations squatting on the Xbox memory window
On the Retroid Pocket Flip2 (kona, kernel 4.19) ART reserves its free-list large object space across 0x754d0000-0x954d0000 at zygote start, covering the fixed Xbox window at 0x80000000, so MAP_FIXED_NOREPLACE fails with EEXIST and the game image never loads.

When the fixed reservation hits EEXIST, munmap exactly the intersection of the requested range with anonymous [anon:dalvik-*] space reservations (idle: those spaces commit lazily from their bottom and this app allocates no large objects) and retry. Nothing changes when the window is already free.
2026-09-30 16:29:26 +13:00
MrBruh f15c1e26b0 Log failed asserts in release builds 2026-09-30 16:18:57 +13:00
MrBruh 5a23aafc3a Merge pull request #24 from ShaneParslow/iso_dialog_hang_fix
Fix Linux hang on data extraction file dialog
2026-09-30 16:02:46 +13:00
MrBruh b739e0a56c Center the zoomed HUD on wide screens 2026-09-30 15:55:13 +13:00
codi 557c0b486c Build the Android port on macOS hosts
configure.py hardcoded the NDK's linux-x86_64 toolchain folder, so the
Android build could not find the NDK on a Mac. Pick the host's folder
(darwin-x86_64, windows-x86_64 or linux-x86_64), or the only one the NDK
has.

On a case-insensitive file system, the game's <StdDef.h> shim found itself
again through its own <stddef.h> inclusion and recursed. Use include_next,
as the port's other header shims do.
2026-09-29 20:51:24 -06:00
Shane Parslow 164d5aebdc Fix Linux hang on data extraction file dialog
SDL_WaitEvent with NULL event does not consume events, so it never
waits, spins, never pumps, never checks dbus. Use PumpEvents and Delay
instead. This also fixes the hot spin that used to occur here.
2026-09-29 19:42:35 -07:00
MrBruh fd8fc72693 Count down the respawn timer on clients 2026-09-30 15:13:55 +13:00
Jonas Volman 8db3414dcf Merge verified canonical reconciliation into halo-1 main
Bring main through 6bfd9567, preserving its existing README exactly. All other tracked content equals the independently gated canonical reconciliation. No force push or source changes beyond the verified canonical commits.
2026-09-29 19:06:08 -07:00
MrBruh 30709dbf0f Fix late joiners missing players and crashing 2026-09-30 13:53:49 +13:00
Jonas Volman 6bfd956737 Reconcile approved Halo fidelity packets and bounded data credit
Recover transparent geometry submit (+1,285 meaningful code, zero exact losses); reconcile COMMON ownership and Q10 85+EXT1 credit, actions/main data, and separately disclosed denominator corrections. Preserve canonical-only repairs and all held research. C09 is ordinary disclosed cleanup, not a claim of proven scaffolding intent. Clean rebuild, 1,663 tests, parks, audits, provider links and independent EXT1 check pass. Local reconciliation only.
2026-09-29 13:52:37 -07:00
MrBruh 0ef2ed7dc7 Extend the network test bots 2026-09-30 03:29:38 +13:00
MrBruh 045d67f861 Close the virtual keyboard with the menus 2026-09-30 03:29:38 +13:00
MrBruh f427d058af Fix statistics refresh order 2026-09-30 03:29:38 +13:00
MrBruh 10644b0f07 Fix client grenade count assert 2026-09-30 03:29:38 +13:00
MrBruh ffa981253c Fix stale action prompt and fallen bodies 2026-09-30 03:29:38 +13:00
MrBruh 2ff6516f02 Fix joining games in progress 2026-09-30 03:29:38 +13:00
MrBruh 84ed39e257 Fix crosshair sprite crash and client update spam 2026-09-30 01:00:43 +13:00
MrBruh 01298f74e9 Send idle actions for empty player slots 2026-09-30 00:33:26 +13:00
MrBruh c43e1ee461 Honour the base vertex index of indexed draws 2026-09-29 23:44:32 +13:00
MrBruh 8f3e7de9ca Play PAL maps as the NTSC maps 2026-09-29 22:07:10 +13:00
Jonas Volman f3aadeab98 Reconcile five Halo functions and approved declaration batches
Integrate coherent B3, P2/K17 prerequisites, completed K1-K16 + S, and the independent profile/effects rulings. Five gains and two explicitly approved B3 debits: net +3 strict functions, +2770 meaningful/+2800 padded bytes (+2774 progress credit). No additional losses; Halo objects 394 to 396 and data unchanged.

Clean rebuild, identical 8252-row stable sweep, 68 valid parks, admission/provider checks, 467-unit warning comparison, unchanged fake scan, and 1318 tests passed. Preserve canonical helper recoveries and unrelated work. Full evidence and disclosures in docs/object_matching_logs/astra_five_function_reconciliation_20260928.md.
2026-09-28 23:17:10 -07:00
MrBruh d8f48a2a34 Fix crosshair drawn before its first update 2026-09-29 17:13:33 +13:00
MrBruh 21714ac086 Name single-player machines after their player 2026-09-29 16:10:02 +13:00
MrBruh 5c867dc46e Fix teams and player slots for mid-match joins 2026-09-29 15:49:22 +13:00
MrBruh caa249457a Install Android maps from a disc image 2026-09-29 15:01:08 +13:00
MrBruh 6013deec33 Fix split-screen scissor and flare counts 2026-09-29 14:35:53 +13:00
MrBruh 1555f895a8 Merge pull request #6 from thelinkin3000/fix/splitscreen-viewport
Fix split-screen showing only the last player's view
2026-09-29 14:31:18 +13:00
MrBruh 8fda61312f Merge pull request #7 from thelinkin3000/fix/android-upload-flag
Stop invalidating streamed buffer ranges on Android
2026-09-29 14:27:40 +13:00
MrBruh a643adea30 Add UPnP for internet play 2026-09-29 14:23:21 +13:00
MrBruh c0c86bda26 Allow joining games in progress 2026-09-29 13:53:13 +13:00
thelinkin3000 96a48d9daa Stop invalidating streamed buffer ranges on Android
The Android port writes each streamed vertex and index range with a
glMapBufferRange call that set both GL_MAP_UNSYNCHRONIZED_BIT and
GL_MAP_INVALIDATE_RANGE_BIT. The two contradict each other: the first
promises that no queued draw is reading the range, the second tells the
driver to discard whatever the range held, which is the work the first exists
to avoid.

Adreno charges for that discard, around 0.85 ms per call whatever the range
written, and the renderer makes one call per draw. On a Galaxy Z Flip 4
(Snapdragon 8 Gen 1) a few hundred of those a frame came to 96% of a 550 ms
frame and left the game running at 1.8 fps with the GPU idle the whole time.
Dropping the invalidation takes the same call to well under a microsecond and
the same game to the display's refresh rate.

Mali is unaffected either way, and its memory use is flat before and after.
2026-09-28 23:37:19 +00:00
MrBruh dddaffd494 Prioritise player updates per client 2026-09-29 12:20:59 +13:00
MrBruh 087c1f5685 Improve netcode, add network versions 2026-09-29 11:32:57 +13:00
Jonas Volman 51cd4d8b67 Reconcile exact AI actor talk-weight chain (+904 bytes) 2026-09-28 13:41:27 -07:00
thelinkin3000 b6ef765346 Fix split-screen showing only the last player's view
In a local two-player match the first player's half of the screen came
out as a flat grey rectangle while the second player's half rendered
correctly; both players' controls still worked.

Each player's window starts by clearing the framebuffer to the colour of
its own fog, expecting the clear to be confined to that window's
rectangle. The port's Direct3D layer instead cleared the whole buffer,
so the second player's setup wiped the picture the first player had
just finished drawing, leaving only its fog colour behind.

Clip those clears to the active window, and make fragment clipping
follow the window too, so a window's effects cannot spill across the
divider. Visibility queries are rescaled to the window as well, since
they now cover a quarter of the area they used to in a two-player game.

Verified on-device in a two-player match.
2026-09-28 17:32:16 +00:00
MrBruh cd47170f39 Fix Android audio deadlock 2026-09-29 03:19:11 +13:00
MrBruh 3c9cd59c1b Add Android app icon 2026-09-29 03:09:03 +13:00
MrBruh c4425807c5 Mark Android app as a game 2026-09-29 03:05:24 +13:00
MrBruh 8994176637 Add auto-updater 2026-09-29 02:51:49 +13:00
MrBruh ed28cb2fa1 Rewrite READMEs, add rolling releases 2026-09-29 01:59:09 +13:00
MrBruh 2055d7216b Add Discord build notifications 2026-09-29 01:40:17 +13:00
MrBruh ef1c625058 Fix guest powerups and pickups 2026-09-29 01:28:47 +13:00
MrBruh 8fcfe9e653 Finish distributed netcode 2026-09-29 00:27:28 +13:00
MrBruh 391e83b11f Add distributed netcode 2026-09-28 22:27:37 +13:00
MrBruh b7874714ae Deterministic FP math 2026-09-28 19:56:00 +13:00
Jonas Volman 5a83a7234a Reconcile exact flag update and multiplayer directions with zero losses 2026-09-27 22:29:15 -07:00
Jonas Volman 709bfc3a3c Reconcile approved frontier ownership and admission packets
Import only independently reviewed production changes. Preserve canonical physics and virtual-keyboard repairs; keep B3 and all unresolved holds out.

Record exactly four approved canary debits (6987 meaningful bytes), six Halo admissions and two revocations. Keep vendor ioinit/storage work separate. Full build, 8252-row sweep, 1318 tests, park/admission/fake audits and 621-object binding pass within approved debit bounds.

Do not publish donor research dumps or private assets. README and unrelated user research remain untouched. See frontier_publication_reconciliation_20260927.md for complete evidence limits and accounting.
2026-09-27 17:12:48 -07:00
Jonas VolmanandClaude Opus 5.5 e59d5ca926 Correct stale vertex-shader initializer matching comment
Owner-approved f0941b08 comment-only reconciliation. Rebuilt object unchanged in every nondebug section, resolved relocation and symbol including line-derived literals. Zero credit/status changes.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:38:04 -07:00
Jonas VolmanandClaude Opus 5.5 646da5e6f7 Reconcile RIFF byte-swap statics without an alignment directive
Approved CH-P1 plus private-storage prerequisite from005f965f/af5c0f7b. Eight later-demo-attested names at January offsets, fourth riff_format_info name disclosed as later only; original riff chunk literal unchanged. Removes invented wrapper, aliases, align directive. Fresh resolved bytes/relocations identical; full build/stable/parks/admission,1318 tests pass. Zero credit.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:36:57 -07:00
Jonas VolmanandClaude Opus 5.5 57591011c5 Reconcile AIFF's six attested private byte-swap objects
Approved CG-P105 plus static prerequisite, from2c6b0292/dac1af35. Later demo PDB names disclosed; January offsets/layout align4 independently checked. All resolved bytes/relocations preserved, alignment8 to4 only. Full build, stable sweep zero losses, parks/admission,1318 tests pass. Zero credit.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:34:21 -07:00
Jonas VolmanandClaude Opus 5.5 09bf46b57a Reconcile key-agreement's six attested private data objects
Approved CG-P07 plus private-storage prerequisite, from c8ea5bf1/faa01ee8. Later demo PDB names disclosed; January offsets0,40,60,80,100,116 and align4 prove layout. Independent resolved relocation checks preserve code/data, only alignment8 to4; no headers/new providers. Production full build, stable sweep zero losses, parks/admission and1318 tests pass. Zero credit.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:32:16 -07:00
Jonas VolmanandClaude Opus 5.5 ef3ec0606c Credit independently verified HS data under approved Q11 model
PD2 from e3c053cd, HS-only append. +53122 raw data+1658 padding=54780, no code/functions/objects. Current provider seals and negative tests pass; full build, stable sweep, parks, admission, 1318 tools tests pass. No scorer upgrade or actions credit.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:29:49 -07:00
Jonas VolmanandClaude Opus 5.5 d7c5c52411 Reconcile reviewed Q11 extent verifier without credit
PB from dccd2e95, including coverage/substitution hardening. Current canonical independent 910-section and20-provider review passed; existing report unchanged without new entry. Full build/stable/parks/admission clean; 1318 tests,5 skipped,100 subtests. Scorer remains pinned3.3.1; no actions credit.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:25:38 -07:00
Jonas VolmanandClaude Opus 5.5 baab1f8efa Reconcile Q11 grouped-data regression gate without credit
Owner-approved PA+PC from c32e53c2. Independent current-object review and negative controls passed. Full build, stable sweep zero gains/losses, parks/admission unchanged; 1199 tests pass. Extent credit and verifier are separate commits.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:22:34 -07:00
Jonas VolmanandClaude Opus 5.5 632bebab28 Reconcile approved main_crash command against canonical
Source from Claude donor d18a7cee; independent stock and W3 builds, full build and stable sweep: +11 meaningful /16 padded Halo code, +1796 data, zero exact losses. 1161 tools tests pass. No declaration-batch or general UB exception.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:15:02 -07:00
Jonas Volman e3aea795e8 Recover private virtual-keyboard helper use and storage 2026-09-27 13:14:25 -07:00
Jonas Volman 25da8a5e32 Restore January water-depth predicate selection in physics 2026-09-27 13:04:02 -07:00
Jonas Volman 30f71413c1 Restore evidenced physics predicates and observer time powers 2026-09-27 12:42:44 -07:00
Jonas Volman 9e851ed4e5 Recover January lift absolute-value operations in physics 2026-09-27 12:25:27 -07:00
Jonas Volman a659684885 Restore two January public BSS definitions with proven owner storage 2026-09-27 12:00:52 -07:00
Jonas Volman ebd24ae1e7 Recover source-backed vector avoidance timer statement without exact credit 2026-09-27 11:44:36 -07:00
6770 changed files with 328265 additions and 1622456 deletions

No files matched your search

+177 -2
View File
@@ -14,12 +14,15 @@ name: Build
# - pushes that only change documentation do not build;
# - Windows, whose minutes cost twice Linux's, builds only Windows;
# - artifacts are kept for three days.
#
# A successful build of main is also published as a GitHub release, the
# newest marked latest, so that
# https://github.com/<repository>/releases/latest/download/<file> always
# gives the latest build; the last five are kept.
on:
push:
paths-ignore:
- "**.md"
- "docs/**"
- "research/**"
workflow_dispatch:
concurrency:
@@ -116,10 +119,30 @@ jobs:
~/.gradle/wrapper
key: gradle-${{ hashFiles('port/android/**/*.gradle*', 'port/android/gradle/wrapper/gradle-wrapper.properties') }}
# the app's signing key, from the ANDROID_KEYSTORE_BASE64 secret (a
# keystore, base64) and ANDROID_KEYSTORE_PASSWORD (its password, and
# the key's, alias halo): every build signed with it installs over the
# one before, as the app's self-updater (Updater.java) needs. Without
# them each build is signed with the runner's own debug key.
- name: Signing key
env:
KEYSTORE: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
run: |
if [ -n "$KEYSTORE" ]; then
printf '%s' "$KEYSTORE" | base64 -d > "$RUNNER_TEMP/halo.keystore"
echo "HALO_ANDROID_KEYSTORE=$RUNNER_TEMP/halo.keystore" >> "$GITHUB_ENV"
else
echo "::warning::no ANDROID_KEYSTORE_BASE64 secret: the Android builds cannot update over each other"
fi
- name: Debug
env:
HALO_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
run: python3 tools/ci_build.py android debug
- name: Release
env:
HALO_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
run: python3 tools/ci_build.py android release
- name: Compile cache
@@ -191,3 +214,155 @@ jobs:
name: halo-windows-release
path: dist/halo-windows-release
retention-days: 3
release:
# The six builds as a release, tagged build-<run number> and marked latest
# (every file keeps its name from release to release, for the README's
# latest/download links); then all but the newest five build-* releases
# go, tags and all. Only for main, and only when every platform built.
needs: [linux, android, windows]
if: ${{ github.ref == 'refs/heads/main' }}
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: write
outputs:
tag: ${{ steps.publish.outputs.tag }}
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
steps:
- uses: actions/download-artifact@v4
with:
pattern: halo-*
path: artifacts
- name: Package
run: |
mkdir release
for build in artifacts/*/; do
name="$(basename "$build")"
(cd "$build" && zip -qr "$GITHUB_WORKSPACE/release/$name.zip" .)
done
ls -l release
- name: Publish
id: publish
env:
MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
tag="build-$GITHUB_RUN_NUMBER"
subject="$(printf '%s' "${MESSAGE:-manual run}" | head -n 1)"
printf '%s\n\nCommit: %s\nBuild: %s\n\nThe release builds are for playing; the debug builds stop at the first failed assertion and log it.\n' \
"$subject" "$COMMIT_URL" "$RUN_URL" > notes.md
gh release create "$tag" release/*.zip --target "$GITHUB_SHA" --latest \
--title "Build $GITHUB_RUN_NUMBER (${GITHUB_SHA:0:7})" --notes-file notes.md
echo "tag=$tag" >> "$GITHUB_OUTPUT"
- name: Keep the last five
run: |
gh release list --limit 100 --json tagName,createdAt \
--jq '[.[] | select(.tagName | startswith("build-"))] | sort_by(.createdAt) | reverse | .[5:] | .[].tagName' |
while read -r tag; do
gh release delete "$tag" --cleanup-tag --yes
done
notify:
# A Discord embed for the run, to the DISCORD_WEBHOOK secret (nothing
# without it): green when every platform built, red when one did not,
# with each platform's result, this run's release (main only), and links
# to the latest release's builds.
needs: [linux, android, windows, release]
if: ${{ !cancelled() }}
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Post to Discord
env:
WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
LINUX: ${{ needs.linux.result }}
ANDROID: ${{ needs.android.result }}
WINDOWS: ${{ needs.windows.result }}
RELEASE_TAG: ${{ needs.release.outputs.tag }}
SERVER: ${{ github.server_url }}
REPOSITORY: ${{ github.repository }}
BRANCH: ${{ github.ref_name }}
SHA: ${{ github.sha }}
ACTOR: ${{ github.actor }}
MESSAGE: ${{ github.event.head_commit.message }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
COMMIT_URL: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}
run: |
if [ -z "$WEBHOOK" ]; then
echo "no DISCORD_WEBHOOK secret: nothing to post"
exit 0
fi
status() {
case "$1" in
success) echo "✅ Built" ;;
failure) echo "❌ Failed" ;;
cancelled) echo "⏹️ Cancelled" ;;
*) echo "➖ Skipped" ;;
esac
}
if [ "$LINUX" = success ] && [ "$ANDROID" = success ] && [ "$WINDOWS" = success ]; then
title="Build succeeded"; color=5763719
else
title="Build failed"; color=15548997
fi
latest="$SERVER/$REPOSITORY/releases/latest"
downloads="[Linux]($latest/download/halo-linux-release.zip) · [Windows]($latest/download/halo-windows-release.zip) · [Android]($latest/download/halo-android-release.zip) ([all builds]($latest))"
if [ -n "$RELEASE_TAG" ]; then
release="[$RELEASE_TAG]($SERVER/$REPOSITORY/releases/tag/$RELEASE_TAG)"
else
release="None (only a successful build of main makes one)"
fi
subject="$(printf '%s' "${MESSAGE:-manual run}" | head -n 1 | cut -c1-200)"
jq -n \
--arg title "$title" --argjson color "$color" --arg url "$RUN_URL" \
--arg description "[\`${SHA:0:7}\`]($COMMIT_URL) $subject — $ACTOR ($BRANCH)" \
--arg release "$release" --arg downloads "$downloads" \
--arg linux "$(status "$LINUX")" --arg windows "$(status "$WINDOWS")" --arg android "$(status "$ANDROID")" \
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'{embeds: [{
title: $title, url: $url, color: $color, description: $description, timestamp: $timestamp,
fields: [
{name: "Linux", value: $linux, inline: true},
{name: "Windows", value: $windows, inline: true},
{name: "Android", value: $android, inline: true},
{name: "Release", value: $release},
{name: "Latest release builds", value: $downloads}
]
}]}' > payload.json
# Discord rate limits webhooks (HTTP 429, with how many seconds to
# wait in retry_after): wait and try again, a few times. A post
# still limited, or a network or Discord error, is a warning rather
# than a failed run; any other answer (a deleted webhook) fails. (At
# most 5 tries of 15 seconds and 4 waits of 30: inside the job's 5
# minutes.)
for attempt in 1 2 3 4 5; do
code=$(curl -sS --max-time 15 -o response.json -w '%{http_code}' \
-H "Content-Type: application/json" -d @payload.json "$WEBHOOK") || code=000
case "$code" in
2??)
exit 0
;;
429)
wait=$(jq -r '.retry_after // 5' response.json 2>/dev/null || echo 5)
wait=$(awk -v w="$wait" 'BEGIN { w += 0; if (w < 1) w = 1; if (w > 30) w = 30; printf "%d", w + 0.999 }')
echo "Discord is rate limiting the webhook; trying again in ${wait}s (attempt $attempt of 5)"
;;
000|5??)
wait=5
echo "Discord answered HTTP $code; trying again in ${wait}s (attempt $attempt of 5)"
;;
*)
echo "::error::Discord answered HTTP $code: $(cat response.json 2>/dev/null)"
exit 1
;;
esac
[ "$attempt" -lt 5 ] && sleep "$wait"
done
echo "::warning::The Discord post did not go through after 5 attempts (HTTP $code)"
+4 -17
View File
@@ -9,36 +9,23 @@ __pycache__
# Build files
build/
projects/
.ninja_*
build.ninja
*.vcxproj*
*.sln*
# decompctx output
ctx.*
*.ctx
# Generated configs
objdiff.json
compile_commands.json
# Miscellaneous
# The January build and the Xbox SDK (tools/xdk_headers.py reads the
# build's PDB); never commit
*.exe
*.pdb
*.idb
*.i64
*.id0
*.id1
*.id2
*.nam
*.til
*.bndb
xbox/
!source/**/xbox/
# Locally extracted proprietary game data (never commit)
assets/
# (but the high-res assets, drawn for the port: tools/hud_assets.py)
!port/assets/
original/
# CI build outputs (tools/ci_build.py)
+109 -46
View File
@@ -1,77 +1,140 @@
Halo 1 xbox decomp, ported to Linux, Windows and Android
=============
# Halo: Combat Evolved for Linux, Windows and Android
This is a port of the decompilation of Halo: Combat Evolved build 2342 (`cachebeta.exe`, sha256 `4cc87b45f721270392a96f1674ed2b5cd4a7bb4355faeab4531d1cf1884d9520`) to Linux, Windows and Android.
[![Join our Discord](https://invidget.switchblade.xyz/9gqcHyr5km)](https://discord.gg/9gqcHyr5km)
<img width="1289" height="995" alt="image" src="https://github.com/user-attachments/assets/0d3ad50f-f8b8-46cf-aef8-e3661da2a7d7" />
This project is a port of the Halo: Combat Evolved decompilation to Linux,
Windows and Android. The decompilation is of the Xbox build 2342
(`cachebeta.exe`, SHA-256
`4cc87b45f721270392a96f1674ed2b5cd4a7bb4355faeab4531d1cf1884d9520`).
This is based on [bnunu](https://github.com/bnunu/halo)'s decompilation project, which itself is a fork of [punpckhdq/halo](https://github.com/punpckhdq/halo).
<img width="1289" height="995" alt="The game on Linux" src="https://github.com/user-attachments/assets/0d3ad50f-f8b8-46cf-aef8-e3661da2a7d7" />
## Agent quick references
The port starts from the decompilation of [bnunu/halo-1](https://github.com/bnunu/halo-1).
That project is a fork of [punpckhdq/halo](https://github.com/punpckhdq/halo).
- [Current campaign house rules and batch/treemap cadence](docs/campaign_house_rules.md)
- [Common constants, types, float patterns, tag IDs, and flag conventions](docs/common_constants.md)
- [Shared assertion macros and byte-matching examples](docs/assertions.md)
- [Supplied CE source maps, recovered names, and next reconstruction packets](docs/user_source_reconstruction_map_20260906.md)
- [Matching methodology and source-credibility rules](docs/matching_methodology.md)
## Download
## Build instructions
GitHub Actions builds the game for each commit. These links download the
builds of the latest release:
You need Python and [ninja-build](https://ninja-build.org/) on your PATH; run `configure.py` from the repository root, then `ninja` with the target below (plain `ninja` builds the one for the computer you are on). No part of the Xbox SDK is needed: the SDK declarations the game uses are in [port/include/xdk](port/include/xdk/README.md).
| Platform | Release | Debug |
| --- | --- | --- |
| Linux | [halo-linux-release.zip](https://github.com/OpenCommunityEdition/OpenCE/releases/latest/download/halo-linux-release.zip) | [halo-linux-debug.zip](https://github.com/OpenCommunityEdition/OpenCE/releases/latest/download/halo-linux-debug.zip) |
| Windows | [halo-windows-release.zip](https://github.com/OpenCommunityEdition/OpenCE/releases/latest/download/halo-windows-release.zip) | [halo-windows-debug.zip](https://github.com/OpenCommunityEdition/OpenCE/releases/latest/download/halo-windows-debug.zip) |
| Android | [halo-android-release.zip](https://github.com/OpenCommunityEdition/OpenCE/releases/latest/download/halo-android-release.zip) | [halo-android-debug.zip](https://github.com/OpenCommunityEdition/OpenCE/releases/latest/download/halo-android-debug.zip) |
Every pushed commit is built by GitHub Actions ([.github/workflows/build.yml](.github/workflows/build.yml)): debug and release builds for Linux, Windows and Android, made by `tools/ci_build.py` (which also works locally, e.g. `python tools/ci_build.py linux release`) and kept as artifacts for three days.
Use the release build to play. The debug build stops at the first failed
assertion and writes it to the log. Use the debug build to find and report
problems.
### Native Linux build
The game updates itself. At start-up it looks for a newer release, and asks
if you want to install it. Refer to "Updates" in
[port/linux/README.md](port/linux/README.md#updates).
`ninja linux` compiles the game with clang into a native 32-bit Linux executable, `build/linux/halo`. It needs clang, 32-bit glibc development files and 32-bit SDL3. It renders with OpenGL, plays sound through SDL3 audio, and takes keyboard, mouse and gamepad input. Put the PAL game data (build 01.01.14.2342) under `assets/` so that `assets/maps` exists, then run `build/linux/halo`. See [port/linux/README.md](port/linux/README.md) for controls and settings.
Each build of the `main` branch that passes on all three platforms is a new
release. The [Releases](https://github.com/OpenCommunityEdition/OpenCE/releases)
page keeps the last five releases. If the latest build has a problem, get
an older build from that page.
### Debug and release
## Game data
The native builds (Linux, Windows, Android) are debug builds by default: like the build the decompilation reproduces, they stop at the first failed assertion and log it. `python configure.py --release` configures release builds instead, which, like the retail game, do not check assertions. The byte-matching build is unaffected.
The port does not include the game data. Download an Xbox disc image
(`.xiso` or `.iso`) of Halo: Combat Evolved. All versions of the game
operate. The maps of the European (PAL) version were made for a slower
console. The port changes them to play as the North American (NTSC) maps do,
so players of the two versions can play together.
### Optimisation
1. Start the game.
2. At the first start, the game asks for the disc image. Select it.
3. The game extracts the `maps/` folder. Then the game starts.
The Linux and Windows builds are optimised for the processor of the computer that builds them (`-march=native`), and may not start on another. `python configure.py --portable` targets the x86-64 baseline instead (SSE2, which every 64-bit x86 processor has): use it for builds you share.
On Linux and Windows, the game puts `maps/` next to the executable. On
Android, copy the disc image to the phone first. The app puts `maps/` in its
data folder. Refer to [port/android/README.md](port/android/README.md).
They also use full link-time optimisation by default, which makes the final link take a minute or so; `--lto=thin` links in parallel and incrementally, `--lto=off` not at all.
## Platforms
They are also optimised with profiles of the game at play, recorded by an instrumented build playing the main menu and the opening minute of every campaign level by itself: `pgo/halo_linux.profdata` for Linux (and Android, whose code is almost the same) and `pgo/halo_windows.profdata` for Windows. The profiles need clang 22 or later; with an older clang, and with `--pgo=off`, the builds do without. A profile stays useful as the code changes (functions it does not know simply go without). To record a new one, delete it and configure with `--pgo=train`: `ninja linux` or `ninja windows` then builds the instrumented game and plays the levels (about fifteen minutes, in a window, silently; it needs the game data in `assets/`) before the real build. Over ssh, Windows plays them on the logged-in user's desktop, through a scheduled task. `--pgo-profile <file>` uses another profile.
Each platform has its own instructions:
With Mesa drivers the Linux build makes its GL calls through Mesa's GL thread (`mesa_glthread`), which takes them off the game's thread.
| Platform | Instructions |
| --- | --- |
| Linux (32-bit x86 executable, OpenGL 4.5, SDL3) | [port/linux/README.md](port/linux/README.md) |
| Windows (32-bit x86 executable, OpenGL 4.5, SDL3) | [port/windows/README.md](port/windows/README.md) |
| Android (arm64 app, OpenGL ES 3, SDL3) | [port/android/README.md](port/android/README.md) |
Frames per second at the opening of a30, uncapped (`vsync = false` in `config.toml`, or `HALO_NO_VSYNC=1`), about 510 draws per frame; each row adds one change to the one above (Linux: a laptop with an Intel Core i7-1355U and Iris Xe graphics, median of three runs; Android: a Pixel 9 Pro XL, Tensor G4, median of three runs):
The Linux README also gives the controls, the settings and the multiplayer
functions. These are almost the same on all platforms.
| Change | Linux | Android |
| --- | ---: | ---: |
| Original | 103 | 132 |
| GL state set only when it changes | 139 | 156 |
| Vertex and index buffers from a GL copy of the Xbox memory | 171 | 193 |
| Full link-time optimisation | 189 | not applicable |
| Profile-guided optimisation | 201 | 197 |
| `-march=native` (`--portable`, x86-64: 212; Android: `-mcpu=cortex-x3`, not used) | 224 | 191 |
| Mesa's GL thread | 257 | not applicable |
| Sound obstruction tested once per game tick, cheaper per-draw bookkeeping | 303 | 220 |
## Multiplayer
Android has no equivalent of `-march=native`: the build runs on any 64-bit phone, and compiling for the Pixel's big cores made it slower there anyway, so its last row builds on the profile-guided one. On the laptop the game's own thread no longer sets the frame rate: it spends about as long waiting for the GL and driver threads and the GPU (which the laptop's power management keeps at a low clock for this load) as they spend waiting for it. The same holds in heavier scenes such as b30, at about 190.
The game can play system link games on a local network and on the internet:
Unity ("jumbo") builds, which compile many files as one, would give the compiler nothing full link-time optimisation does not already see, and the decompiled sources declare too many conflicting local types for it anyway. `-O3` and profile-driven function splitting measured no faster than `-O2`.
- A system link game can have up to 128 players on up to 128 machines.
- Linux, Windows and Android machines can play in the same game.
- An invite link lets a machine join a game on the internet. No server of
this project is necessary.
- The netcode is new. Each machine moves its own player at once,
and the host makes the decisions for the game. Refer to
[port/linux/NETCODE.md](port/linux/NETCODE.md).
### Frame rate
## Build the game
The native builds draw a frame at every refresh of the display (60, 90, 120, 240 Hz, ...), paced by vsync, while the game still simulates at 30 Hz as on the Xbox: each frame blends the last two ticks. To see the frame rate, open the developer console (the \` key) and enter `display_framerate true`; the frames per second, averaged over half a second, appear at the bottom right of the screen. `interpolation = false` in `config.toml` (written next to the executable, or in the data folder on Android, on the first run) restores the original 30 frames per second. See [port/linux/README.md](port/linux/README.md#frame-rate).
You do not need the Xbox SDK. The port supplies the SDK declarations that
the game uses. Refer to [port/include/xdk](port/include/xdk/README.md).
### Native Windows build
To build the game:
`ninja windows`, run on Windows, compiles the game with clang into a native 32-bit Windows executable, `build/windows/halo.exe` (with `SDL3.dll`), sharing the Linux build's platform layer. It needs LLVM, Python and ninja, plus Visual Studio's x86 C++ libraries and a Windows SDK. Put the game data under `assets/` as for Linux. See [port/windows/README.md](port/windows/README.md).
1. Install Python and [ninja](https://ninja-build.org/).
2. Install the tools for your platform. Refer to the README for the
platform.
3. In the root folder of the repository, enter `python configure.py`.
4. Enter `ninja` with the target for the platform:
### Android build
| Target | Result |
| --- | --- |
| `ninja linux` | `build/linux/halo` |
| `ninja windows` (on Windows) | `build/windows/halo.exe` and `SDL3.dll` |
| `ninja android_apk` | `port/android/app/build/outputs/apk/debug/app-debug.apk` |
`ninja android_apk` builds an arm64 Android app (`port/android/app/build/outputs/apk/debug/app-debug.apk`) that runs the game natively on 64-bit ARM phones, with OpenGL ES 3 rendering at the device's aspect ratio, SDL3 audio and game controller support (including a PS5 DualSense over Bluetooth). It needs the Android NDK and a clang with the `arm64_32` target in addition to the Linux build's requirements. The game data goes in the app's storage (the app offers to import it). See [port/android/README.md](port/android/README.md).
If you enter `ninja` without a target, ninja builds the game for the
computer that you use.
### Matching build
`tools/ci_build.py` makes the same builds as GitHub Actions. For example,
enter `python tools/ci_build.py linux release`.
This fork builds only the native ports. The upstream project's byte-matching build, which compiles the game with the Xbox SDK's own compiler and compares it with `cachebeta.exe`, needs the August 2001 Xbox SDK, which cannot be redistributed, so `configure.py` no longer writes it. Its sources, configuration and `#ifdef`s are kept as they are, so that upstream's matching work still merges; `SolutionConfig.matching` in `tools/project_x86.py` turns it back on, for a checkout with the SDK's `XDK/xbox` folder extracted to `xbox/` and `cachebeta.exe` in the repository root. See [port/linux/README.md](port/linux/README.md#the-matching-build-on-a-linux-host) for running it on Linux.
### Build options
## Where's all the type information?
Give these options to `configure.py`:
We use debug symbols from later Halo games to help map structures, enums, type definitions, function signatures and some variable names. You should obtain a copy of the Halo CEA beta and run it through [pdb-decompiler](https://github.com/camden-smallwood/pdb-decompiler) to obtain debug info for yourself. Note that CEA is based off of Halo PC and has its own modifications which make it not 100% accurate to the original Xbox title.
| Option | Result |
| --- | --- |
| (none) | A debug build. A failed assertion stops the game. |
| `--release` | A release build. The game does not examine assertions, as in the retail game. |
| `--portable` | The Linux and Windows builds operate on all x86-64 processors. Use this option for builds that you give to other persons. |
| `--lto=thin`, `--lto=off` | Less link-time optimization. The link is faster. |
| `--pgo=off` | No profile-guided optimization. |
| `--pgo=train` | Records a new optimization profile. Refer to "Optimization profiles". |
Without `--portable`, the Linux and Windows builds use all the instructions
of the processor that builds them (`-march=native`). Such a build does not
always start on a different computer.
### Optimization profiles
The builds use profiles of the game to optimize the code:
- `pgo/halo_linux.profdata` for Linux and Android.
- `pgo/halo_windows.profdata` for Windows.
The profiles need clang 22 or later. With an older clang, the builds do not
use the profiles.
To record a new profile:
1. Delete the profile.
2. Enter `python configure.py --pgo=train`.
3. Enter `ninja linux` or `ninja windows`.
The build then plays the main menu and the first minute of each campaign
level. This procedure continues for approximately 15 minutes. The game
data must be in `assets/`.
-1304
View File
File diff suppressed because it is too large. Load diff
-22457
View File
File diff suppressed because it is too large. Load diff
-45
View File
@@ -1,45 +0,0 @@
{
"version": 1,
"entries": [
{
"unit": "source/rasterizer/xbox/rasterizer_xbox_profile",
"class": "source-layout-incomplete",
"symbol": "_rasterizer_profile_globals",
"reason": "All 16 target functions and all 2234 target data bytes are exact. The three stock SDK select-any tables (D3DTEXTUREDIRECTENCODE 16 bytes, D3DSIMPLERENDERSTATEENCODE 328, D3DPRIMITIVETOVERTEXCOUNT 88) come from D3D8.h's unconditional extern const selectany definitions, which January's TU included (its object imports _D3DDevice_InsertCallback@12 and QueryPerformance*). Each copy is byte-identical to January's selected provider and passes the selected-provider pair links in both orders, the standard under which collision_debug and the other Matching emitters carry the same tables. Canonical's dynavobgeom reconciliation (641e1466, docs/object_matching_logs/claude_fifty_objects_canonical_reconciliation.md) still leaves stock SDK-table coalescing open pending a uniform owner policy audit, so this object inherits that decision. Whole-object admission also fails source authenticity. The private state is written as four invented file-static aggregates (rasterizer_profile_globals, rasterizer_profile_elapsed_state, rasterizer_profile_frame_state, rasterizer_profile_state). Their pad02/pad06/pad12/pad16 members only spell out VC7's per-static 4-byte alignment padding, and reserved06[6] hides one unattested 4-byte object at .bss +0x45c. Loose file statics reproduce every code byte and every other data offset, and the later /Od build places elapsed_times before callback_start_times, contradicting the elapsed_state member order. The rasterizer_profile_check NONE branch reproduces January's format/varargs defect (%s consumes the NONE profile value), which awaits the owner's original-bug ruling. Three load-bearing volatile qualifiers have no first-party attestation. The TU also still declares global_window_parameters with a consumer-local extern: moving it to the owner header rasterizer_xbox_internal.h keeps every exact section byte-identical but declares the name earlier in rasterizer_xbox_models, which moves the residual __rasterizer_model_draw by one instruction.",
"evidence": "research/fifty_objects_20260925/w/review3_rasterizer_xbox_profile/REVIEW.md",
"reopen": "Recover the genuine January private state shape: replace the invented aggregates with authentic separate file statics, or first-party-attested structures, without fabricated pad/reserved members. Identify the unreferenced 4-byte January object at .bss +0x45c from first-party evidence rather than a filler. Obtain the owner's rulings on the BUG-marked NONE-branch varargs defect and the three volatile qualifiers. Any new private names need the owner's descriptive-name policy (as for the held rasterizer_xbox .bss split). Preserve all 16 target functions and all data sections, land the pinned 132-byte .data entry only with the Matching flip, move the window extern to its owner header with a full consumer sweep (re-baselining the rasterizer_xbox_models __rasterizer_model_draw park it perturbs), and apply the owner's uniform stock SDK-table decision from the open dynavobgeom reconciliation."
},
{
"unit": "source/rasterizer/xbox/rasterizer_xbox_dynavobgeom",
"class": "candidate-only-comdat-owner",
"symbol": "_D3DSIMPLERENDERSTATEENCODE",
"reason": "All 17 January code functions and all 31 January-owned sections/symbols match after replacing local debug, window and pixel-shader views with genuine owner types. Nine D3DINLINE wrappers have January's file-static storage. The two surplus color-conversion helpers are byte-identical to January's selected providers, and all 17 surplus symbols pass selected-provider links in both orders. The global_window_parameters extern still lives in consumer C files, with incompatible partial views in other consumers rather than a common owner declaration. Three stock SDK tables and other candidate-only constants/literals remain absent from January's split; the selected-provider test ignores unrelated unresolved symbols. A complete ordinary final-link ownership proof has not been established, so exact function credit does not imply Matching.",
"evidence": "docs/object_matching_logs/rasterizer_xbox_dynavobgeom_owner_layout_reconciliation.md",
"reopen": "Move global_window_parameters to its authentic owner declaration and reconcile the partial consumer views with a full regression sweep. Establish selected ownership/coalescing of candidate-only SDK tables and helper data through a complete compatible ordinary link or equally decisive January source/link evidence, without losing target exactness. Do not suppress genuine header emissions, alter selection metadata, force-inline, hand-copy helpers or count candidate-only bytes as January-owned."
},
{
"unit": "source/interface/hud_draw",
"class": "candidate-only-comdat-owner",
"symbol": "_fast_ftol",
"reason": "The exact flash-duration leaf uses the authentic shared conversion helper and naturally emits an additional unreferenced select-any32-byte fast_ftol copy absent from January HUD Draw. January's identical selected Actor Combat copy is no-duplicates; its ordinary XDK duplicate-owner failure is already documented. The two newly exact drawing helpers receive valid target-function credit, but no whole-object or linked-coalescing claim follows.",
"evidence": "docs/object_matching_logs/hud_draw_obj_two_exact_leaves_integration_20260904.md#complete-owner-boundary",
"reopen": "Require authentic source topology or an ordinary base/base link with an authenticated compatible canonical selected helper before clearing this veto. Do not force inline, manually expand math, alter target/COMDAT metadata, invent a discard pass, or credit candidate-only helper bytes. All remaining HUD Draw functions/data/linkage/source checks must independently pass before whole-object Matching."
},
{
"unit": "source/effects/decals",
"class": "candidate-only-comdat-owner",
"symbol": "_vector_from_points2d",
"reason": "The exact typed decal_projection_create body naturally inlines vector_from_points2d as January requires, but VC7 also emits an unreferenced select-any helper body absent from the January Decals split. January's selected Action Obey copy is no-duplicates. The exact target function receives valid credit; the surplus helper receives none and blocks whole-object Matching.",
"evidence": "docs/object_matching_logs/decals_obj_color_projection_packet_20260912.md#inline-helper-ownership-boundary",
"reopen": "Remove only when authentic source topology or an ordinary compatible canonical link preserves decal_projection_create while preventing or safely coalescing the extra helper. Do not force inline, manually expand the helper for code shaping, alter COMDAT metadata, or count the candidate-only bytes."
},
{
"unit": "source/effects/decals",
"class": "candidate-only-comdat-owner",
"symbol": "_cross_product2d",
"reason": "The exact typed decal_projection_create body naturally inlines cross_product2d as January requires, but VC7 also emits an unreferenced select-any helper body absent from the January Decals split. January's selected Actor Combat copy is no-duplicates. The exact target function receives valid credit; the surplus helper receives none and blocks whole-object Matching.",
"evidence": "docs/object_matching_logs/decals_obj_color_projection_packet_20260912.md#inline-helper-ownership-boundary",
"reopen": "Remove only when authentic source topology or an ordinary compatible canonical link preserves decal_projection_create while preventing or safely coalescing the extra helper. Do not force inline, manually expand the helper for code shaping, alter COMDAT metadata, or count the candidate-only bytes."
}
]
}
-1162
View File
File diff suppressed because it is too large. Load diff
@@ -1,20 +0,0 @@
{
"debug_sections": [
{
"after_fingerprint_sha256": "8e07b48ebedfa52de730be35f0b56d2fadb9b72e51a1edd029e20b5bb5cddc23",
"before_fingerprint_sha256": "92288b56cfbca8760857b38d5fb34cdfe3b56c092891ebfa6c00dc9cfc68b395",
"recipe": "xdk-stock-d3dinline",
"section": ".debug$F|anonymous=2",
"unit": "source/rasterizer/xbox/rasterizer_xbox_plasma_energy"
},
{
"after_fingerprint_sha256": "5b79f3d7f6d7cd9eca0a4324f66baf87dff523b69e917689707607178a0b48ab",
"before_fingerprint_sha256": "fd054221f7d9970be62ba370272c65e7c78a8a116110be3a5796fb06a8127ece",
"recipe": "xdk-stock-d3dinline",
"section": ".debug$S|anonymous=0",
"unit": "source/rasterizer/xbox/rasterizer_xbox_plasma_energy"
}
],
"functions": [],
"schema_version": 1
}
@@ -1,102 +0,0 @@
{
"debug_sections": [
{
"after_fingerprint_sha256": "8e07b48ebedfa52de730be35f0b56d2fadb9b72e51a1edd029e20b5bb5cddc23",
"before_fingerprint_sha256": "92288b56cfbca8760857b38d5fb34cdfe3b56c092891ebfa6c00dc9cfc68b395",
"recipe": "xdk-stock-d3dinline",
"section": ".debug$F|anonymous=2",
"unit": "source/rasterizer/xbox/rasterizer_xbox_text"
}
],
"functions": [
{
"after_comdat_selection": 2,
"after_fingerprint_sha256": "458fb5bd5e078ea76d95e47dd9632bb3bb0b3a25c8aa64efba41fd359a99b2cf",
"before_comdat_selection": 1,
"before_fingerprint_sha256": "9ff19872aa908361445bece0a21cab0ca4c93f68cb7bad1bcc70e77f0de6c6c8",
"function": "_D3DDevice_SetRenderState",
"recipe": "xdk-stock-d3dinline",
"source_recipe": "stock XDK D3DINLINE (static __forceinline)",
"target_evidence_sha256": "ef767bb47681ea399daf6f8720f056e523790d105bcea1f634d71c3e3611d192",
"unit": "source/rasterizer/xbox/rasterizer_xbox_text"
},
{
"after_comdat_selection": 2,
"after_fingerprint_sha256": "459f5d33ded34255e79f255c42a193c74066bdad08fc9116b2dd82eaf16ee094",
"before_comdat_selection": 1,
"before_fingerprint_sha256": "142d63782a1e9e3a41a5f722084b8681b1bf765912c4dd91784299a280112204",
"function": "_D3DDevice_SetTextureStageState",
"recipe": "xdk-stock-d3dinline",
"source_recipe": "stock XDK D3DINLINE (static __forceinline)",
"target_evidence_sha256": "ef19cb2f99aa7c1ad0f06f57c44a2dbace4b75ba6c779e68a1885e832d50d6c4",
"unit": "source/rasterizer/xbox/rasterizer_xbox_text"
},
{
"after_comdat_selection": 2,
"after_fingerprint_sha256": "5382805b884f0d40e945936f88f42d2b3c9f0c5ca0f83098312bc5a8444c3d66",
"before_comdat_selection": 1,
"before_fingerprint_sha256": "8f5385e5727b96b51ea06b98ff74fc9ba2f57802f0118f6f759e32ed3bb60797",
"function": "_IDirect3DDevice8_Begin@8",
"recipe": "xdk-stock-d3dinline",
"source_recipe": "stock XDK D3DINLINE (static __forceinline)",
"target_evidence_sha256": "f9f8776473104153fbb44b071659641050b0ef17ffde33740e053f1d31598d86",
"unit": "source/rasterizer/xbox/rasterizer_xbox_text"
},
{
"after_comdat_selection": 2,
"after_fingerprint_sha256": "a6c5208bf1949404ef5f4340bb14eff5ffab46a37c3aa041c2d5e4523b1dd8c2",
"before_comdat_selection": 1,
"before_fingerprint_sha256": "7916c2492802dcf917429ec316aefe67fbd8c96ef60bad99e12e26c38271342a",
"function": "_IDirect3DDevice8_End@4",
"recipe": "xdk-stock-d3dinline",
"source_recipe": "stock XDK D3DINLINE (static __forceinline)",
"target_evidence_sha256": "2acf41df227cc5ab56a634bc22b9a1af2f3594aa640b8a26e2126783c6802ed2",
"unit": "source/rasterizer/xbox/rasterizer_xbox_text"
},
{
"after_comdat_selection": 2,
"after_fingerprint_sha256": "09a181160ca4a0cec88563ed68f170bb7562d98e07cae82eb6ede383067e867c",
"before_comdat_selection": 1,
"before_fingerprint_sha256": "41c0a05e408be665a1bb43ea64bc56c1dbca0c5ef460f81b4248f6d02a2999d3",
"function": "_IDirect3DDevice8_SetTextureStageState@16",
"recipe": "xdk-stock-d3dinline",
"source_recipe": "stock XDK D3DINLINE (static __forceinline)",
"target_evidence_sha256": "b53ab8eb325c14f08df0e98df7b272a3dceb09921ed0d1dbeea49b69e07557f2",
"unit": "source/rasterizer/xbox/rasterizer_xbox_text"
},
{
"after_comdat_selection": 2,
"after_fingerprint_sha256": "13ff5c4aa2773bc0df32f62fce0afe58261ebabf2b2ca76426ef01a613fa7bf0",
"before_comdat_selection": 1,
"before_fingerprint_sha256": "7bb9a9967e3ddbbadfd0667b85c8f52ee06137a1f0b0cd4a0e45a4e1247d1815",
"function": "_IDirect3DDevice8_SetVertexData2f@16",
"recipe": "xdk-stock-d3dinline",
"source_recipe": "stock XDK D3DINLINE (static __forceinline)",
"target_evidence_sha256": "1303752bb188cf6b5a00adb8c2c49092f22408c2c38203e8daddf5b61aa61153",
"unit": "source/rasterizer/xbox/rasterizer_xbox_text"
},
{
"after_comdat_selection": 2,
"after_fingerprint_sha256": "78b5829871e22251b4e266bd81caa2eda338a6e93f43c2a592c93eaad8808e6b",
"before_comdat_selection": 1,
"before_fingerprint_sha256": "464ecff7dc1e2d1a1c18d02c467dcbe893baedfebfa42920922583cde75b9331",
"function": "_IDirect3DDevice8_SetVertexDataColor@12",
"recipe": "xdk-stock-d3dinline",
"source_recipe": "stock XDK D3DINLINE (static __forceinline)",
"target_evidence_sha256": "e4a0591ba36b4fdad4a24f1009fed7b141c8acf919c8a4bca421435760c2e38a",
"unit": "source/rasterizer/xbox/rasterizer_xbox_text"
},
{
"after_comdat_selection": 2,
"after_fingerprint_sha256": "c936826f1cdc47b8e52662c867164d8fa9063b86bd7050db06a3992a0414dba7",
"before_comdat_selection": 1,
"before_fingerprint_sha256": "db0e0cd1dc136a05abf97c28072edfda4b9cd8de3a0bf1e45fd45747655dcef5",
"function": "_IDirect3DDevice8_SetVertexShaderConstant@16",
"recipe": "xdk-stock-d3dinline",
"source_recipe": "stock XDK D3DINLINE (static __forceinline)",
"target_evidence_sha256": "cc42796796437d1eaee75aa6b6a300dc712d6d48cb2571820adf13f5eea7a3bd",
"unit": "source/rasterizer/xbox/rasterizer_xbox_text"
}
],
"schema_version": 1
}
-113827
View File
File diff suppressed because it is too large. Load diff
-4
View File
@@ -1,4 +0,0 @@
{
"version": 1,
"entries": []
}
-581
View File
@@ -1,581 +0,0 @@
[
{
"unit": "source/shell/shell_xbox",
"group": "aligned-rdata",
"reason": "the complete rebuilt object owns the exact two string COMDATs and compiler-generated SEH scope table; the grouped verifier proves each payload, alignment-derived padded extent, flags, owner snapshot, relocation address/type, and source-relative handler destination while objdiff merges the aligned sections and attributes the private scope-table labels differently",
"members": [
{
"symbol": "??_C@_06GAOAIMNN@xdemo?5?$AA@",
"measurements": {
"target": {
"section": ".rdata",
"size": 7,
"padded_size": 8,
"flags": 1076891712,
"relocation_count": 0,
"normalized_sha256": "2d636beaf6ef18262eb352ea8a00f3b1b0ee3628666eb07e679892eca5f08b5e",
"owner": { "value": 0, "type": 0, "storage": 2 }
},
"base": {
"section": ".rdata",
"size": 7,
"padded_size": 8,
"flags": 1076891712,
"relocation_count": 0,
"normalized_sha256": "2d636beaf6ef18262eb352ea8a00f3b1b0ee3628666eb07e679892eca5f08b5e",
"owner": { "value": 0, "type": 0, "storage": 2 }
}
}
},
{
"symbol": "??_C@_06PFFNFMJI@XDEMOS?$AA@",
"measurements": {
"target": {
"section": ".rdata",
"size": 7,
"padded_size": 8,
"flags": 1076891712,
"relocation_count": 0,
"normalized_sha256": "798ec2f79c908eed7153344688b0def452386891f2deae50826a2658182e9bd5",
"owner": { "value": 0, "type": 0, "storage": 2 }
},
"base": {
"section": ".rdata",
"size": 7,
"padded_size": 8,
"flags": 1076891712,
"relocation_count": 0,
"normalized_sha256": "798ec2f79c908eed7153344688b0def452386891f2deae50826a2658182e9bd5",
"owner": { "value": 0, "type": 0, "storage": 2 }
}
}
},
{
"symbol": "_rdata_002a0a88",
"base_symbol": "$T18302",
"source_function": "_main",
"measurements": {
"target": {
"section": ".rdata",
"size": 12,
"padded_size": 16,
"flags": 1077940288,
"relocation_count": 2,
"normalized_sha256": "3414421b183188342209566fad4e0c0723070b4030630dde685ba70281e92725",
"owner": { "value": 0, "type": 0, "storage": 2 }
},
"base": {
"section": ".rdata",
"size": 12,
"padded_size": 16,
"flags": 1077940288,
"relocation_count": 2,
"normalized_sha256": "3414421b183188342209566fad4e0c0723070b4030630dde685ba70281e92725",
"owner": { "value": 0, "type": 0, "storage": 3 }
}
}
}
]
},
{
"unit": "libs/libcmt/crt0msg",
"symbol": "_rterrs",
"reason": "The first-party XDK names the private runtime-error table rterrs. January and the rebuilt object have identical 144-byte data, all 18 relocation sites and types, and independently resolved string destinations; objdiff undercredits the aggregate despite exact COFF section comparison.",
"measurements": {
"size": 144,
"relocation_count": 18,
"normalized_sha256": "25298d4d1ea85dfa6a4696577658216429876d7b7b59ed245667db872d9d79d2"
}
},
{
"unit": "source/camera/editor_flying_camera",
"group": "camera-static-sections",
"allow_incomplete_unit": true,
"credit_raw_size": true,
"reason": "January and the rebuilt object have identical 124-byte private .bss and 52-byte .data payloads, flags, alignments, and owner storage. The nine .data relocations resolve to the same image destinations. HCEX names the separate file statics and their definition order; the January .bss offsets corroborate their exact layout. Objdiff undercredits the two sections after the authentic private-state split. Credit raw sizes because its report scores .bss as 124 bytes, not the 128-byte alignment-derived padded extent.",
"members": [
{
"symbol": "_is_scripted",
"measurements": {
"target": {
"section": ".bss", "size": 124, "padded_size": 128,
"flags": 3225419904, "relocation_count": 0,
"normalized_sha256": "7b8ec8dd836b564f0c85ad088fc744de820345204e154bc1503e04e9d6fdd9f1",
"owner": { "value": 0, "type": 0, "storage": 3 }
},
"base": {
"section": ".bss", "size": 124, "padded_size": 128,
"flags": 3225419904, "relocation_count": 0,
"normalized_sha256": "7b8ec8dd836b564f0c85ad088fc744de820345204e154bc1503e04e9d6fdd9f1",
"owner": { "value": 0, "type": 0, "storage": 3 }
}
}
},
{
"symbol": "_editor_camera_speed",
"measurements": {
"target": {
"section": ".data", "size": 52, "padded_size": 52,
"flags": 3224371264, "relocation_count": 9,
"normalized_sha256": "22486dfe6b955caf557fcbca418621432912c2d3012afd4e6031fe22b880d07b",
"owner": { "value": 0, "type": 0, "storage": 3 }
},
"base": {
"section": ".data", "size": 52, "padded_size": 52,
"flags": 3224371264, "relocation_count": 9,
"normalized_sha256": "22486dfe6b955caf557fcbca418621432912c2d3012afd4e6031fe22b880d07b",
"owner": { "value": 0, "type": 0, "storage": 3 }
}
}
}
]
},
{
"unit": "source/units/unit_dialogue",
"symbol": "_global_speech_priority_names",
"reason": "January and the rebuilt 44-byte speech-name table have identical payload, 11 relocation sites and types, and independently resolved literal destinations; objdiff undercredits the table because the executable split assigns nine folded SELECT_ANY strings to other objects",
"measurements": {
"size": 44,
"relocation_count": 11,
"normalized_sha256": "85759b3811ff7dc47b03792ac85317be51431a3f9e01dcafce317ed736a391b0"
}
},
{
"unit": "source/game/game_allegiance",
"symbol": "_global_game_team_names",
"reason": "The 40-byte January and rebuilt team-name tables have identical normalized payload, owner storage and flags, and all ten relocations resolve independently to the same image destinations; objdiff undercredits this sole unmatched section.",
"measurements": {
"size": 40,
"relocation_count": 10,
"normalized_sha256": "2c34ce1df23b838c5abf2a7f6437cca3d3067ed509ff25f11df6b11b582b51eb"
}
},
{
"unit": "source/game/game_engine_slayer",
"symbol": "_slayer_engine",
"reason": "The 136-byte January and rebuilt engine tables have identical normalized payload, external owner storage and section flags, and all 24 relocations resolve independently to the same image destinations; objdiff undercredits this sole unmatched section.",
"measurements": {
"size": 136,
"relocation_count": 24,
"normalized_sha256": "cd47b29508fea4e4c11451792c17f629a9ad240b3c3df5c889c0d416c378bc78"
}
},
{
"unit": "source/interface/hud_definitions",
"symbol": "_global_hud_anchor_names",
"reason": "The 20-byte January and rebuilt HUD anchor-name tables have identical normalized payload, owner storage and flags, and all five relocations resolve independently to the same image destinations; objdiff undercredits this sole unmatched section.",
"measurements": {
"size": 20,
"relocation_count": 5,
"normalized_sha256": "de47c9b27eb8d300dbb5f2c353e632c393262cf06340c4fa7f1b40c4cbd36f90"
}
},
{
"unit": "source/structures/leaf_map",
"symbol": "_leaf_map_initialize_section",
"allow_incomplete_unit": true,
"reason": "January and rebuilt profile-section data have identical 2512-byte payload, section flags and static owner, with all 28 relocations independently resolving to the same image destinations; objdiff undercredits this sole unmatched section.",
"measurements": {
"size": 2512,
"relocation_count": 28,
"normalized_sha256": "34570fc43b6cae2795503a6a62861e9a42ce856cb74d895ba7fa6174f1bda68b"
}
},
{
"unit": "source/game/game_engine_race",
"symbol": "_race_engine",
"reason": "January and rebuilt race-engine tables have identical 136-byte payload, section flags and external owner, with all 25 relocations independently resolving to the same image destinations; objdiff undercredits this sole unmatched section.",
"measurements": {
"size": 136,
"relocation_count": 25,
"normalized_sha256": "97a4c632a978acafd20933da4504d8ec3fdf3c5afd2f78934bced57dc8f746f4"
}
},
{
"unit": "source/game/game_engine_ctf",
"symbol": "_ctf_engine",
"reason": "January and rebuilt CTF-engine tables have identical 136-byte payload, section flags and external owner, with all 28 relocations independently resolving to the same image destinations; objdiff undercredits this sole unmatched section.",
"measurements": {
"size": 136,
"relocation_count": 28,
"normalized_sha256": "45c41730d038fd3f985f7ccfd8a7e04aa02e2db549ac941386e7cd04c1e1d8c1"
}
},
{
"unit": "source/bitmaps/libtiff/tif_print",
"symbol": "_tif_print_rcsid",
"reason": "The 164-byte January and rebuilt libtiff data section has identical payload, flags and external owner, and all 18 relocations independently resolve to the same image destinations; objdiff undercredits its sole unmatched section.",
"measurements": {
"size": 164,
"relocation_count": 18,
"normalized_sha256": "a81bb4fa00f599a245c1e6b636a2a630629557f2c86f92e2960cf3e16ed94e23"
}
},
{
"unit": "source/memory/zlib/zutil",
"symbol": "_z_errmsg",
"reason": "The 44-byte January and rebuilt zlib error table has identical payload, flags and external owner, and all ten relocations independently resolve to the same image destinations; objdiff undercredits its sole unmatched section.",
"measurements": {
"size": 44,
"relocation_count": 10,
"normalized_sha256": "966ef500de11387122d16ed112b63eb8562f81fc0e67d6f8f4d190766cf1e6ae"
}
},
{
"unit": "source/memory/zlib/deflate",
"symbol": "?my_version@?1??deflateInit2_@@9@9",
"reason": "The four-byte January and rebuilt zlib version pointer has identical payload, flags and static owner; its relocation independently resolves to the same image destination despite objdiff's alias attribution.",
"measurements": {
"size": 4,
"relocation_count": 1,
"normalized_sha256": "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"
}
},
{
"unit": "source/networking/network_server_manager",
"symbol": "_network_game_server_memory_do_not_use_directly_in_use",
"reason": "January and rebuilt eight-byte BSS owner have identical extent, flags, external storage and section-relative layout with no relocations; objdiff undercredits this sole unmatched section.",
"measurements": {
"size": 8,
"relocation_count": 0,
"normalized_sha256": "af5570f5a1810b7af78caf4bc70a660f0df51e42baf91d4de5b2328de0e83dfc"
}
},
{
"unit": "source/game/game_engine_oddball",
"symbol": "_oddball_engine",
"reason": "January and rebuilt Oddball engine tables have identical 136-byte payload, flags and owner storage, with all 27 relocations independently resolving to the same image destinations; objdiff undercredits the sole unmatched data section.",
"measurements": {
"size": 136,
"relocation_count": 27,
"normalized_sha256": "ffe1ef0aa827017276299f3a497655019d91f94374129c31634061500e5ae4b9"
}
},
{
"unit": "source/game/game_globals",
"symbol": "_global_material_type_strings",
"reason": "January and rebuilt material-name tables have identical 132-byte payload, flags and owner storage, with all 33 relocations independently resolving to the same image destinations; objdiff undercredits the sole unmatched data section.",
"measurements": {
"size": 132,
"relocation_count": 33,
"normalized_sha256": "115bad14f1c9f2c027a84de21b107015722cb76be8d0abf3760ad8e00d6c24a5"
}
},
{
"unit": "source/items/projectile_definitions",
"symbol": "_default_projectile_material_response",
"reason": "January and rebuilt projectile material tables have identical 160-byte payload, flags and owner storage, with all three relocations independently resolving to the same image destinations; objdiff undercredits the sole unmatched data section.",
"measurements": {
"size": 160,
"relocation_count": 3,
"normalized_sha256": "b71e2246c2d39831ad65f2b9570fecb59a1a0d743278b63cc125b118c148f6b9"
}
},
{
"unit": "source/memory/byte_swapping",
"symbol": "_byte_bs_codes",
"reason": "January and rebuilt byte-swap tables have identical 144-byte payload, flags and owner storage, with all eight relocations independently resolving to the same image destinations; objdiff undercredits the sole unmatched data section.",
"measurements": {
"size": 144,
"relocation_count": 8,
"normalized_sha256": "fc892f5c359e0165aa8cdbd745b191928baac3213935c79d8699cdcba32468d0"
}
},
{
"unit": "source/physics/collision_bsp_definitions",
"symbol": "_global_collision_bsp_definition_data",
"reason": "January and rebuilt collision BSP definition tables have identical 900-byte payload, flags and owner storage, with all 68 relocations independently resolving to the same image destinations; objdiff undercredits the sole unmatched data section.",
"measurements": {
"size": 900,
"relocation_count": 68,
"normalized_sha256": "3ed97b7cf09b09837ca36461d74c21909d34106d563a7b914aaffd7c51cf3e56"
}
},
{
"unit": "source/physics/collision_usage",
"symbol": "_global_collision_function_names",
"reason": "January and rebuilt collision function-name tables have identical 138-byte payload, flags and owner storage, with all 30 relocations independently resolving to the same image destinations; objdiff undercredits the sole unmatched data section.",
"measurements": {
"size": 138,
"relocation_count": 30,
"normalized_sha256": "804797955ced17a1f6b1e4b86c0a83a5d25fcb03fa846e690dae51ef13bc5c2f"
}
},
{
"unit": "source/render/render_particles",
"symbol": "_render_particles_section",
"reason": "The January and rebuilt render-particles profile-section owners have equal 1528-byte payload, section flags and external storage, and their sole relocation independently resolves to the same image string; objdiff undercredits the complete section. All three target functions are strict exact, and base-only SELECT_ANY literals have byte-identical January-selected providers with passing ordinary pair links.",
"measurements": {
"size": 1528,
"relocation_count": 1,
"normalized_sha256": "806638334fff1341360b4dec7db0987871f376bc3999e26ff4bcd3a1755d2955"
}
},
{
"unit": "source/bitmaps/bitmaps",
"symbol": "_global_vector_palette",
"reason": "January and rebuilt 1116-byte palette data have equal payload, section flags, owner storage and offsets, and all 21 relocations resolve to the same image destinations. All 34 target functions are strict exact; the sole extra fast_ftol COMDAT is byte-identical to its selected January provider and pair-links in either order.",
"measurements": {
"size": 1116,
"relocation_count": 21,
"normalized_sha256": "443063631e6958ff5026be3038f8785d61eee4073cb01b8eaa4fdfcc3b620002"
}
},
{
"unit": "source/game/cheats",
"symbol": "_cheat_strings",
"reason": "January and rebuilt cheats BSS have equal 3201-byte extent, section flags, external owner, zero relocations and matching section-relative symbol layout. The two private function names come from the first-party HCEA SHIP PDB; the string array has a descriptive inferred name, and the trailing unreferenced byte is disclosed as name-unknown rather than credited as a separate owner. All 14 target functions are strict exact and candidate-only providers pass bounded links in both orders.",
"measurements": {
"size": 3201,
"relocation_count": 0,
"normalized_sha256": "76d6c353527d212ec3799ea375f945e3ae3760257bd2ef5a75441090df0ddfac"
}
},
{
"unit": "source/items/items",
"symbol": "_item_update_section",
"reason": "January and rebuilt item-update profile owners have equal 1528-byte payload, section flags, external owner and one resolved image relocation. All 18 target functions and eight target-owned noncode symbols are strict exact. The only former link conflict was the hand-written add_vectors3d provider in ActorCombat; its genuine shared-header inline now emits the same code as SELECT_ANY without changing any ActorCombat function or park. All 42 candidate-only helper/literal copies equal their January-selected providers and 26 ordinary provider-pair links pass in both orders.",
"measurements": {
"size": 1528,
"relocation_count": 1,
"normalized_sha256": "806638334fff1341360b4dec7db0987871f376bc3999e26ff4bcd3a1755d2955"
}
},
{
"unit": "source/interface/hud",
"symbol": "_default_string",
"reason": "January and rebuilt HUD own the same four-byte external data pointer, section flags and relocation destination: the two-zero-byte string selected from GameEngine. All 22 target functions are strict exact. Five candidate-only genuine shared-header inline helpers match their January-selected copies byte-for-byte and ordinary provider-pair links pass in both orders, so these folded copies receive no separate credit.",
"measurements": {
"size": 4,
"relocation_count": 1,
"normalized_sha256": "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"
}
},
{
"unit": "source/effects/particles",
"symbol": "_particles_update_section",
"reason": "January and rebuilt particles own the same 1536-byte static data section with three image-equivalent relocations, flags and payload. Its eight-byte collision-scale bounds also have identical payload, flags and the external linkage January requires. All 20 target functions are strict exact; genuine shared-header helper providers now link in both orders after removing duplicate hand-written bodies from DebugMemory, Contrails and BoredCamera. Candidate-only folded helper copies receive no separate credit.",
"measurements": {
"size": 1536,
"relocation_count": 3,
"normalized_sha256": "6d9398e131ef72b9a6ffc2321c22d27a3e2db1a458385b77b08ba0657a56c74f"
}
},
{
"unit": "source/ai/ai_scenario_definitions",
"symbol": "_global_ai_default_state_names",
"reason": "January and rebuilt 48-byte AI default-state name table (sole .data section) have identical normalized payload and flags (c0300040), a PDB-public external owner at offset 0, and all 12 relocations resolve to the same image addresses. The whole object otherwise matches: 4/4 functions strict exact, every January-owned section and symbol equal, and all candidate-only literals pair-link with their selected providers in both orders. objdiff 3.3.1 misreads the identical '$' literal names.",
"measurements": {
"size": 48,
"relocation_count": 12,
"normalized_sha256": "17b0761f87b081d5cf10757ccc89f12be355c70e2e29df288b65b30710dcbcd1"
}
},
{
"unit": "source/ai/ai_profile",
"symbol": "_global_ai_meter_definitions",
"reason": "January and rebuilt 236-byte AI meter/profile-name .data section (sole data section; _ai_profile_render_spray_names static at +224 on both sides) have identical normalized payload and flags (c0400040), and all 10 relocations resolve to the same image addresses. The whole object otherwise matches: 23/23 functions strict exact, every January-owned section and symbol equal and consistent with cachebeta.pdb publics, and candidate-only literals pair-link with their selected providers in both orders.",
"measurements": {
"size": 236,
"relocation_count": 10,
"normalized_sha256": "3a69a8b8a83037c3277fa86c09c7aa4e9b9da8b480e3facb87275548ee4c88b8"
}
},
{
"unit": "source/shaders/shader_definitions",
"symbol": "_global_shader_effect_additive",
"reason": "January and rebuilt 364-byte shader-effect .data section (the additive and alpha-blended effect definitions, the latter at +184 on both sides) have identical normalized payload and flags (c0400040), and both relocations resolve to the same image addresses. The whole object otherwise matches: 1/1 function strict exact, every January-owned section and symbol equal and consistent with cachebeta.pdb publics, and candidate-only literals pair-link with their selected providers in both orders.",
"measurements": {
"size": 364,
"relocation_count": 2,
"normalized_sha256": "8bbb21a994d2ec5051de3ea76497f648469fe42d5f6aa7da85f3b598bf16bcd0"
}
},
{
"unit": "source/units/dialogue_definitions",
"symbol": "_dialogue_vocalization_type_name",
"reason": "January and rebuilt 1,672-byte dialogue .data section have identical normalized payload and flags (c0400040), and all 418 relocations resolve to the same image addresses. The whole object otherwise matches: 2/2 functions strict exact, all 298 January symbols equal in section, offset and storage and consistent with cachebeta.pdb publics, and all candidate-only literals pair-link with their selected providers in both orders.",
"measurements": {
"size": 1672,
"relocation_count": 418,
"normalized_sha256": "256984cb62bc879971463ff0ff7567042dc95d8b0fb2bc676f246c2293c77ae8"
}
},
{
"unit": "source/ai/actor_types",
"symbol": "_global_actor_type_names",
"reason": "January and rebuilt 128-byte actor-type .data section (the static actor_type_definitions table at +0 and the public global_actor_type_names at +64) have identical normalized payload and flags (c0400040), and all 32 relocations resolve to the same image addresses. Ownership was corrected first: cachebeta.pdb publics prove actor_type_definitions and its accessor actor_type_definition_get file-static. January's own assert strings name actor_type_definitions; the Halo CE atlas names the accessor at the same 0x140-byte span. All 13 functions are strict exact and every January-owned section and symbol matches.",
"measurements": {
"size": 128,
"relocation_count": 32,
"normalized_sha256": "38723a2e5e8a17aa7950dc008209944e898f69a7bd10a23c839d341e935fd5ca"
}
},
{
"unit": "source/cutscene/recorded_animation_definitions",
"symbol": "_recorded_animation_event_stream_data",
"reason": "January and rebuilt 180-byte recorded-animation tag .data section have identical normalized payload and flags (c0400040), and all 11 relocations resolve to the same image addresses. Ownership was corrected first: byte_swap_recording is absent from cachebeta.pdb publics and referenced by no other January object, so it is file-static. Both functions are strict exact and every January-owned section and symbol matches.",
"measurements": {
"size": 180,
"relocation_count": 11,
"normalized_sha256": "c83e71991a0ac6309173102325bf6a194d27d5d80cdc9a395ed954c80bae3d5d"
}
},
{
"unit": "source/models/model_animation_definitions",
"symbol": "_weapon_type_animation_list",
"reason": "January and rebuilt 1,288-byte animation-list .data section have identical normalized payload and flags (c0400040), and all 170 relocations resolve to the same image addresses. Ownership was corrected first: the seven *_animation_list_entries arrays are absent from cachebeta.pdb publics and referenced by no other January object, so they are file-static beside their public animation_list wrappers. All three functions are strict exact and all 139 January symbols match.",
"measurements": {
"size": 1288,
"relocation_count": 170,
"normalized_sha256": "76f074030d3cb6940a74673504f664555bd5d02087282c01338c8b83fe3d1654"
}
},
{
"unit": "source/sound/sound_classes",
"symbol": "_sound_classes",
"reason": "January and rebuilt 2,452-byte sound-class .data section have identical normalized payload and flags (c0400040), and all 51 relocations resolve to the same image addresses. Ownership was corrected first: the static accessor at 0x1b84c0 is absent from cachebeta.pdb publics and is named sound_class_datum_get by the Halo CE atlas, anchored on six already-exact sound_classes symbols. All 12 functions are strict exact and every January-owned section and symbol matches.",
"measurements": {
"size": 2452,
"relocation_count": 51,
"normalized_sha256": "1ae64de7abb95c40952d3aacf88c68232070d4c19906bf32dfc8877bd6725abc"
}
},
{
"unit": "source/objects/object_types",
"symbol": "_object_data_definition",
"reason": "January and rebuilt 2,608-byte object-type tag .data section have identical normalized payload and flags (c0400040), and all 190 relocations resolve to the same image addresses. Ownership was corrected first: HCEX.pdb names the sole object_types file static processed_bsp_flags (unsigned short), absent from cachebeta.pdb publics. All 31 functions are strict exact and all 66 January symbols match. All eight candidate-only helper COMDATs are byte-identical to January's selected copies and pair-link in both orders; items' hand-written valid_real_matrix4x3/valid_real_vector3d_axes3 NODUP providers were replaced by the genuine real_math.h inlines.",
"measurements": {
"size": 2608,
"relocation_count": 190,
"normalized_sha256": "205043014fcdf8e45147d168ae70e43cda7814dfc63f1c1dade93f2c0f9f4341"
}
},
{
"unit": "source/hs/hs_scenario_definitions",
"symbol": "_hs_script_types_enum",
"reason": "January and rebuilt HS scenario .data have identical 628-byte normalized payload, all 33 relocation identities, and 8-byte section alignment (c0400040). The original nine public definitions retain their target offsets and storage. Separate typed field and byte-swap arrays replace invented packed aggregates and padding; the byte-swap function is genuinely file-static. The one target function remains strict exact, and there are no surplus external definitions.",
"measurements": {
"size": 628,
"relocation_count": 33,
"normalized_sha256": "a1add12fc2ddd7afb3935997f9e1532b27a3887f8e85e914db954d1665547c09"
}
},
{
"unit": "source/hs/hs_globals_external",
"symbol": "_screenshot_size_definition",
"reason": "January and rebuilt HS external-global .data have identical 7,076-byte normalized payload, section flags (c0400040), and all 1,322 relocation identities. The invented 442-element definition aggregate has been replaced by 442 separate file-static typed definitions in January's proven 12-byte record order; the public hs_external_globals table remains at +5304. The 2011 first-party HCEX PDB independently attests 427 unique definition names and their type; the 15 older-only names are descriptive names derived from their January string records, not claimed originals. January and rebuilt first-record storage are file-static, and the unit emits no code or candidate-only external owner.",
"measurements": {
"size": 7076,
"relocation_count": 1322,
"normalized_sha256": "1e4346aef4491a55772f4f51a6989e06074dc5245a2c01aa1be97fb2e221ae8c"
}
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"symbol": "_event_handler_functions",
"reason": "January and rebuilt 934-byte event-handler .data section (the static event_handler_function_list name/function table at +0 and the public event_handler_functions at +816) have identical normalized payload and flags (c0400040), and all 228 relocations resolve to the same image addresses. The 366-byte .bss is also identical. Ownership was corrected first: 98 handler functions, event_handler_function_list and new_campaign_profile_name are absent from cachebeta.pdb publics and referenced by no other January object, so they are file-static. All 100 functions are strict exact and all 452 January symbols match. The three stock D3D SDK .rdata tables it also emits are byte-identical to January's selected copies and pair-link in both orders, as in the 54 already-admitted objects that emit them.",
"measurements": {
"size": 934,
"relocation_count": 228,
"normalized_sha256": "c1040d667c392fd772fab34335ade456dac49ae864a8200c4560df63cec186fd"
}
},
{
"unit": "source/objects/objects",
"symbol": "?section@?1??objects_update@@9@9",
"reason": "objects_update's function-static section table: January and the rebuilt object have identical 1,530-byte .data, relocation site/type and independently resolved string destination; objdiff 3.3.1's '$'-literal relocation-name defect undercredits it despite exact COFF section comparison. Whole object admitted after the Layer 1 provider repairs (action_charge, player_control, bored_camera, path_obstacles, decals) replaced hand-written NODUP helper copies with the genuine real_math.h inlines: all 121 functions strict exact, 225 symbols match storage/offsets and cachebeta.pdb publics, 26 candidate-only helper COMDATs byte-identical to January's selected copies and pair-linking in both orders.",
"measurements": {
"size": 1530,
"relocation_count": 1,
"normalized_sha256": "e94bb79478c95dce11c4a1f720a07ccbaa3a8d18b84acbe5605472d6f8635b74"
}
},
{
"unit": "source/units/units",
"symbol": "_magic_base_animation_seat_index",
"reason": "units' writable .data section (magic_base_animation_seat_index, unit_update_section, unit_update's last_time and base_seat_labels): January and the rebuilt object have identical 1,564-byte payload, 7 relocation sites/types and independently resolved string destinations (5 of the 7 base_seat_labels strings are selected in other January objects and pair-link cleanly); objdiff 3.3.1's '$'-literal relocation-name defect undercredits it despite exact COFF section comparison. Whole object admitted: all 189 functions strict exact; the 25 code_ placeholders carry their first-party names (HCEX_Release.pdb units compiland, Sept-2001 map, January atlas) and all 50 file-statics carry static storage per cachebeta.pdb publics, so 321 symbols match storage/offsets; 97 candidate-only surplus definitions (31 helper COMDATs byte-identical to January's selected copies, SDK tables, literals) pair-link with their selected providers in both orders; _point_from_line3d is referenced out of line by January's units.obj (owner ruling 20260921 #1).",
"measurements": {
"size": 1564,
"relocation_count": 7,
"normalized_sha256": "6ed61c39a648905cf23b12b916e2adadfe97b6e5aa492c29a14c7265809ef1a7"
}
},
{
"unit": "source/items/projectiles",
"symbol": "_projectile_update_section",
"allow_incomplete_unit": true,
"reason": "January and rebuilt projectile-update profile-section data have identical 1548-byte payload, section flags and static owners (_projectile_update_section at 0, _effect_marker_names at 1528), with all six relocations independently resolving to the same image destinations; January's split leaves the pooled normal and gravity literals undefined while the rebuilt object emits identical select-any copies, and objdiff 3.3.1's '$'-literal name defect undercredits this sole unmatched section. The unit's .rdata is already 100%; the parked _projectile_new residual keeps the unit incomplete and receives no credit from this entry.",
"measurements": {
"size": 1548,
"relocation_count": 6,
"normalized_sha256": "7bfaf5fb0b0c9e0593153885a81788bc8b118a9651a60a8a94e5ffd9fd203cf2"
}
},
{
"unit": "source/math/periodic_functions",
"symbol": "_global_periodic_functions_enum_strings",
"allow_incomplete_unit": true,
"reason": "The January and rebuilt 96-byte periodic/transition enum .data have identical normalized payload, owner storage and flags, and all 20 relocations resolve to the same image destinations. Objdiff 3.3.1 undercredits folded string literals. The variable-period builder remains parked and earns no code credit.",
"measurements": {
"size": 96,
"relocation_count": 20,
"normalized_sha256": "8fdd4bf9e8be002fe8938ee0849dfec244c27138f7b77eff28ec8ba7b4538614"
}
},
{
"unit": "source/objects/object_lights",
"symbol": "_lights_section",
"allow_incomplete_unit": true,
"reason": "object_lights' writable .data (the static lights_section profile section at +0 and the four public object_light_* tuning globals at +1528..+1540): January and the rebuilt object have identical 1,541-byte payload, owner storage and flags (c0400040), and its single relocation (the 'render_lights' string, selected in hs) resolves independently to the same image destination; objdiff 3.3.1's '$'-literal relocation-name defect undercredits it despite exact COFF section comparison. All 43 functions are strict exact; should_render_lights, light_attenuation and cluster_get_first/next_light are file-static per cachebeta.pdb publics (no January reference outside the object; should_render_lights is the /Od-attested call in lights_render_diffuse and lights_render_specular), so all 71 January symbols match storage and offsets; the 12 candidate-only helper COMDATs pair-link with their selected providers in both orders. The unit is not marked Matching only because its _object_get_bounding_sphere COMDAT differs from action_vehicle's selected copy in its two __FILE__ literal relocations (January object_lights' own 'c:\\halo\\source\\objects\\objects.h' literal; held owner item), so this sole unmatched section is credited with an explicit incomplete-unit opt-in.",
"measurements": {
"size": 1541,
"relocation_count": 1,
"normalized_sha256": "527b43a173f528444fa4423af92d7d58a83f2afe7fc1661b6d297401b2760f71"
}
},
{
"unit": "source/bitmaps/bitmap_group",
"symbol": "_global_bitmap_reference",
"allow_incomplete_unit": true,
"reason": "January and rebuilt bitmap_group writable .data (the public global_bitmap_reference/_optional, bitmap_pixel_data, color_plate_data and bitmap_group tag definitions plus their static field/enum tables, 31 owners) have identical 1,424-byte payload, flags (c0400040) and owner storage, and all 152 relocations independently resolve to the same image destinations. January's split leaves the empty-string and 'default' literals undefined (linker-selected in other objects) while the rebuilt object emits identical select-any copies, so objdiff 3.3.1's '$'-literal name defect undercredits this sole unmatched section; renaming only the '$' inside literal names on both sides scores it 100.0 with the production objdiff binary. The parked _bitmap_group_add_bitmap residual keeps the unit incomplete, so this entry opts in explicitly as an incomplete unit and grants no function or object credit.",
"measurements": {
"size": 1424,
"relocation_count": 152,
"normalized_sha256": "df5c6d69f4e13ea6df9398e2b5cc5c4455410b077b5ba6763930c4dd748505cb"
}
},
{
"unit": "source/ai/ai_communication",
"symbol": "_global_communication_priority_names",
"allow_incomplete_unit": true,
"reason": "January and rebuilt ai_communication .data (the public global_communication_priority_names, global_communication_team_names and global_communication_type_names string tables at +0/+32/+48) have identical 276-byte payload, flags (c0400040) and owner storage, and all 69 relocations independently resolve to the same image destinations. January's split leaves six literals ('none', 'shout', 'damage', 'cover', 'flee', 'berserk') undefined because the linker selected other objects' copies, while the rebuilt object emits identical select-any copies, so objdiff 3.3.1's '$'-literal name defect undercredits this sole unmatched section; renaming only the '$' inside literal names on both sides scores it 100.0 with the production objdiff binary. _ai_communication_actor_talk_weight (parked) and _ai_communication_event remain residual, so this entry opts in explicitly as an incomplete unit and grants no function or object credit.",
"measurements": {
"size": 276,
"relocation_count": 69,
"normalized_sha256": "d489001ff88ed206b56f44bd0789a8332510f33bf40a312a7b2e5d60c3fe3e03"
}
},
{
"unit": "source/ai/ai_debug",
"symbol": "_global_ai_debug_firing_position_color_count",
"allow_incomplete_unit": true,
"reason": "January and rebuilt ai_debug .data (the public global_ai_debug_firing_position_color_count at +0, the file-static postcombat type-name table at +4 and the two file-static selected-squad globals at +48/+52) have identical 56-byte payload, flags (c0300040) and owner storage, and all 11 relocations independently resolve to the same image destinations. January's split leaves the 'none' literal undefined because the linker selected another object's copy, while the rebuilt object emits an identical select-any copy, so objdiff 3.3.1's '$'-literal name defect undercredits this sole unmatched section; renaming only the '$' inside literal names on both sides scores it 100.0 with the production objdiff binary. _ai_debug_render_actor remains residual, so this entry opts in explicitly as an incomplete unit and grants no function or object credit.",
"measurements": {
"size": 56,
"relocation_count": 11,
"normalized_sha256": "3ba0a54a0875e71a01972b13dcf574a4d16d0f1ea3531ef51d98311b5ac7eee5"
}
},
{
"unit": "source/game/game_engine_king",
"symbol": "_king_engine",
"allow_incomplete_unit": true,
"reason": "January and rebuilt King engine tables have identical 136-byte payload, section flags (c0400040) and public owner storage (_king_engine is a cachebeta.pdb public), and all 23 relocations (22 engine callbacks and the 'king' name literal) independently resolve to the same image destinations. January's split leaves the 'king' literal undefined because the linker selected game_engine's copy, while the rebuilt object emits an identical select-any copy, so objdiff 3.3.1's '$'-literal name defect undercredits this sole unmatched section; renaming only the '$' inside literal names on both sides scores it 100.0 with the production objdiff binary. All 29 functions are strict exact; the unit stays NonMatching until the owner rules on the whole-object packet (owner queue #13: _find_next_hill's preserved uninitialised no-candidate return, plus the cachebeta storage fix), so this entry opts in explicitly as an incomplete unit, credits only the table's data bytes and certifies nothing about _find_next_hill or object completion.",
"measurements": {
"size": 136,
"relocation_count": 23,
"normalized_sha256": "4009f5b6117e9f00cb001a23b9a56b9dc51a694206c442ce715e2bed2d5465a7"
}
}
]
-587
View File
@@ -1,587 +0,0 @@
[
{
"unit": "source/devices/devices",
"function": "_device_export_function_values",
"reason": "The scoped Opus reconciliation proves all 512 padded bytes, 21 ordered relocations and normalized SHA-256 7bf0963941ed36b13676536592a3af9d2786f5d4970b56f1129d24c3a35cc968 exact. Objdiff's 94.405594 percent is a compiler-local switch-label representation false negative; credit only the target report's 505 meaningful bytes. The ordinary typed mode loop preserves January's unordered delay predicate and uses the proper devices.h public declaration. No comparator relaxation, byte patch, extra helper, parameter-width trick or whole-object completion claim. See docs/object_matching_logs/devices_obj_export_function_values_opus_reconciliation_20260906.md."
},
{
"unit": "source/rasterizer/xbox/rasterizer_xbox",
"function": "_rasterizer_set_stencil_mode",
"reason": "The scoped Opus reconciliation proves all 784 padded/meaningful bytes, 78 ordered relocations and normalized SHA-256 903201d61d15f0c4dcad96fd81acb8049e8bfc201883061924dc95f3eaea8852 exact. Objdiff's 96.65072 percent is a compiler-local switch-label representation false negative. Source applies the debug policy in the public long input domain before narrowing once to the short cached mode, as January's entry dataflow corroborates; no declaration permutation or type/ABI change. Existing data and helper ownership remain separate from this function credit. See docs/object_matching_logs/rasterizer_xbox_obj_three_natural_opus_closures_20260906.md."
},
{
"unit": "source/ai/action_obey",
"function": "_action_obey_command_end",
"reason": "Independent frozen January/actual COFF comparison proves all 560 padded bytes, 24 ordered relocations and normalized SHA-256 f59f0185a672ce6409fe3d3c3a439e517483593f6ffc9bf5eff4b18dd4420a61 exact. Objdiff reports 85.26786 percent because compiler-local switch labels are represented differently. The ledger credits only the target's 549 meaningful bytes, with no byte/comparator exception. The genuine private body is retained through individual_end, not a fake external ABI. Fable's earlier case-order correction remains documented; this is independently authenticated reconstruction, not a claim of first-shot source. Whole-object math-owner and remaining-source boundaries are separate. See docs/object_matching_logs/action_obey_obj_partial_graph_reconciliation_20260905.md."
},
{
"unit": "source/ai/action_obey",
"function": "_action_obey_directmovement_update_facing",
"reason": "Independent frozen January/actual COFF comparison proves all 336 meaningful/padded bytes, 17 ordered relocations and normalized SHA-256 17b28fabb3ad4366074689d2f9be1364ccf07cdc0c7a185dbd4dff25b00e3f97 exact. Objdiff reports 96.77419 percent due to compiler-local switch labels. This is strict target-function credit only; no comparator weakening or surplus helper credit. Its real individual_update caller naturally preserves the target cross_product3d/negate_vector3d owners after removing their manual wrapper aliases. Five additional header math owners are explicitly audited and remain a whole-object link/admission boundary. See docs/object_matching_logs/action_obey_obj_partial_graph_reconciliation_20260905.md."
},
{
"unit": "source/input/input_xbox",
"function": "_input_key_is_down",
"reason": "Independent root and reviewer COFF gates prove all 192 meaningful/padded bytes and 19 ordered relocations exact, normalized SHA-256 c1ab3b0043e411c7e6695fb129837501c3bca12a2477d9497b5123f3eaf17805. Objdiff reports 93.93939 percent because compiler-local switch labels are represented differently. A natural result variable and single terminal return satisfy the house rules without a bogus unreachable fallback and reproduce January; no target-byte exception or declaration-order tuning. See docs/object_matching_logs/input_xbox_obj_public_leaves_independent_review_20260904.md."
},
{
"unit": "source/interface/player_ui",
"function": "_set_local_player_controls_from_player_profile",
"reason": "Independent strict COFF comparison proves all 528 meaningful/padded bytes and 14 ordered relocations exact, normalized SHA-256 ff47c65c6eda0c31b625817bb914f77ad790244d2be44109e484d10664685ee5. Raw objdiff reports 95.89041 percent due to compiler-local switch labels. This repairs existing function credit only; it does not count fresh reconstruction or waive the object's public playlist-layout/coalescing boundary. See docs/object_matching_logs/player_ui_obj_owner_house_rule_reconciliation_20260904.md."
},
{
"unit": "source/interface/hud_unit",
"function": "_hud_render_damage_indicators",
"reason": "Independent root/worker COFF gates prove all 688 padded bytes and 39 ordered relocations exact, normalized SHA-256 cf7cb8af78f4f4f9fb2a11c7bb67c2e5fb0e31cedcbe8b062ef7aa7fad4cacd0. Objdiff reports 95.85253 percent because compiler-local switch labels are represented differently; no byte or target exception. The independently PDB/type/ABI-reviewed source and complete emitted-owner audit are documented in docs/object_matching_logs/hud_unit_obj_damage_indicators_exact_20260904.md."
},
{
"unit": "source/networking/network_client_message_handler",
"function": "_network_game_client_handle_message",
"reason": "Audited January dispatcher is strict exact: 1088 padded/logical bytes, 84 ordered relocations, normalized SHA-256 32ca599a7f9f3eb44546bcd119ce77eb705e9fc745f9d852c16295cbe1f3850f. Objdiff differs only on compiler-local switch labels; no byte or target exception. See docs/object_matching_logs/network_client_message_handler_obj_admission_review_20260904.md."
},
{
"unit": "source/cache/cache_files",
"function": "_cache_files_map_directory",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/units/units",
"function": "_unit_is_busy",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/text/international_strings",
"function": "_double_byte_character",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/shaders/shaders",
"function": "_numeric_countdown_timer_get",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/shaders/shaders",
"function": "_shader_get_vertex_shader_permutation",
"reason": "hardened COFF comparison proves all 288 padded bytes and all 18 semantic relocations exact; objdiff reports a local jump-table label encoding false negative"
},
{
"unit": "source/shaders/shaders",
"function": "_shader_is_decal",
"reason": "hardened COFF comparison proves all 144 padded bytes and all 10 semantic relocations exact; objdiff reports a local jump-table label encoding false negative"
},
{
"unit": "source/rasterizer/xbox/rasterizer_xbox_errors",
"function": "_rasterizer_error",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/networking/network_messages",
"function": "_create_network_game_message",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/networking/network_game_globals",
"function": "_network_game_client_start_frame",
"reason": "hardened COFF comparison proves all 560 padded bytes and all 57 semantic relocations exact; objdiff reports a local jump-table label encoding false negative"
},
{
"unit": "source/memory/data_encoding",
"function": "_data_decode_array",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/memory/data_encoding",
"function": "_data_decode_memory",
"reason": "hardened COFF comparison proves all 304 padded bytes and all 19 semantic relocations exact; objdiff reports a local jump-table label encoding false negative"
},
{
"unit": "source/memory/data_encoding",
"function": "_data_encode_memory",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/memory/data_encoding",
"function": "_data_encode_array",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/memory/data_packets",
"function": "__data_packet_verify",
"reason": "hardened COFF comparison proves all 464 padded bytes and all 27 semantic relocations exact; objdiff reports a local jump-table label encoding false negative"
},
{
"unit": "source/memory/data_packets",
"function": "__data_packet_encode",
"reason": "hardened COFF comparison proves all 768 padded bytes and all 44 semantic relocations exact; objdiff reports a local jump-table label encoding false negative"
},
{
"unit": "source/memory/data_packets",
"function": "__data_packet_decode",
"reason": "hardened COFF comparison proves all 672 padded bytes and all 28 semantic relocations exact; objdiff reports a local jump-table label encoding false negative"
},
{
"unit": "source/memory/byte_swapping",
"function": "__byte_swap_data",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/memory/zlib/inflate",
"function": "_inflate",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/main/console",
"function": "_console_update",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/interface/ui_widget_text_search_and_replace_functions",
"function": "_widget_controller",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/interface/attract_mode",
"function": "_attract_mode_get_localized_movie_path",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/game/game_time",
"function": "_game_time_start",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/game/game_time",
"function": "_game_time_update",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/devices/device_controls",
"function": "_code_00083e30",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/cseries/cseries_windows",
"function": "_code_0007cb70",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bungie_net/network/transport_address",
"function": "_transport_error_to_string",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_print",
"function": "_TIFFPrintDirectory",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_packbits",
"function": "_PackBitsEncode",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_next",
"function": "_NeXTDecode",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_dir",
"function": "_code_00053960",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_dir",
"function": "_code_00054160",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_dirwrite",
"function": "_TIFFWriteNormalTag",
"reason": "hardened COFF comparison proves all 800 padded bytes and all 30 semantic relocations exact; objdiff reports an internal jump-table relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_dirwrite",
"function": "_TIFFWriteDirectory",
"reason": "hardened COFF comparison proves all 1760 padded bytes and all 56 semantic relocations exact; objdiff reports internal jump-table relocation encoding false negatives"
},
{
"unit": "source/bitmaps/libtiff/tif_aux",
"function": "_TIFFVGetFieldDefaulted",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_lzw",
"function": "_horizontalAccumulate8",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_lzw",
"function": "_horizontalAccumulate16",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_lzw",
"function": "_horizontalDifference8",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_lzw",
"function": "_horizontalDifference16",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_getimage",
"function": "_setorientation",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_getimage",
"function": "_code_00058a20",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_getimage",
"function": "_code_00058c40",
"reason": "hardened COFF comparison proves all 832 padded bytes and all 18 semantic relocations exact; objdiff reports static BSS symbol/relocation ownership false negatives"
},
{
"unit": "source/bitmaps/libtiff/tif_getimage",
"function": "_code_00059180",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_getimage",
"function": "_put1bitbwtile",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_getimage",
"function": "_code_00059df0",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_getimage",
"function": "_code_0005a6f0",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_getimage",
"function": "_TIFFReadRGBAImage",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_dirread",
"function": "_TIFFFetchData",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_dirread",
"function": "_TIFFFetchByteArray",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_dirread",
"function": "_TIFFFetchNormalTag",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_dirread",
"function": "_TIFFReadDirectory",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_sentinel",
"function": "_sentinel_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_mounted_weapon",
"function": "_mounted_weapon_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_marine",
"function": "_marine_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_jackal",
"function": "_jackal_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_infection",
"function": "_infection_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_hunter",
"function": "_hunter_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_grunt",
"function": "_grunt_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_flood",
"function": "_flood_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_engineer",
"function": "_engineer_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_elite",
"function": "_elite_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_crew",
"function": "_crew_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/actor_type_carrier",
"function": "_carrier_decide_action",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/action_guard",
"function": "_action_guard_speak_post_combat",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/action_guard",
"function": "_action_guard_setup_postcombat",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/ai/action_guard",
"function": "_action_guard_control",
"reason": "hardened COFF comparison proves exact bytes and semantic relocations; objdiff reports a local-label/relocation encoding false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_fax3",
"function": "_skiptoeol",
"reason": "hardened COFF comparison proves exact bytes and internal jump-table relocations; objdiff reports a local-label ownership false negative"
},
{
"unit": "source/bitmaps/libtiff/tif_fax3",
"function": "_Fax3Decode2DRow",
"reason": "hardened COFF comparison proves exact bytes and uniquely anchored non-code relocation destinations; objdiff reports csplit data-anchor naming false negatives"
},
{
"unit": "source/objects/objects",
"function": "_object_compute_node_matrices",
"reason": "hardened COFF comparison proves all 6560 padded bytes and all 318 semantic relocations exact; objdiff reports a local-label encoding false negative"
},
{
"unit": "source/objects/objects",
"function": "$L11841",
"owner_function": "_attachments_new",
"reason": "the exact owning COMDAT and an internal relocation prove this differently named jump-table label is the same destination"
},
{
"unit": "source/objects/objects",
"function": "$L9776",
"owner_function": "_object_export_function_values",
"reason": "the exact owning COMDAT and an internal relocation prove this differently named jump-table label is the same destination"
},
{
"unit": "source/objects/objects",
"function": "$L9764",
"owner_function": "_object_export_function_values",
"reason": "the exact owning COMDAT and an internal relocation prove this differently named jump-table label is the same destination"
},
{
"unit": "source/objects/objects",
"function": "$L10620",
"owner_function": "_attachments_delete",
"reason": "the exact owning COMDAT and an internal relocation prove this differently named jump-table label is the same destination"
},
{
"unit": "source/memory/zlib/infblock",
"function": "$L2587",
"owner_function": "_inflate_blocks",
"reason": "the exact owning COMDAT and an internal relocation prove this differently numbered jump-table label is the same destination"
},
{
"unit": "source/rasterizer/xbox/rasterizer_xbox_vertex_shaders_runtime",
"function": "_rasterizer_set_vertex_shader_permutation",
"reason": "hardened COFF comparison proves all 1376 padded bytes and all 133 semantic relocations exact; objdiff reports a switch-table/local-label encoding false negative"
},
{
"unit": "source/networking/telnet_console",
"function": "_process_telnet_client_buffer",
"reason": "hardened COFF comparison proves all 496 padded bytes and all 30 semantic relocations exact; objdiff reports a switch-table/local-label encoding false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_set_game_engine",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_change_ctf_rules",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_change_koth_rules",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_change_slayer_rules",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_change_oddball_rules",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_change_racing_rules",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_change_player_options",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_change_item_options",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_initialize_game_engine",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_initialize_ctf_rules",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_initialize_koth_rules",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_initialize_slayer_rules",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_initialize_oddball_rules",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_initialize_racing_rules",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_initialize_player_options",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_playlist_profile_initialize_item_options",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_player_profile_initialize_controller_settings",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/interface/ui_widget_event_handler_functions",
"function": "_player_profile_change_controller_settings",
"reason": "hardened COFF comparison proves exact bytes and relocation destinations; objdiff reports a local-symbol ownership false negative"
},
{
"unit": "source/render/render_contrails",
"function": "_render_contrail",
"reason": "hardened COFF comparison proves all 1952 padded bytes and all 57 semantic relocations exact (SHA-256 70d28d083222f55ac171cb1aa9676d018fb2bcae9ee54f09facee54755cef5e8); objdiff reports a switch-table/local-label encoding false negative"
},
{
"unit": "source/items/weapons",
"function": "_weapon_set_state",
"reason": "hardened COFF comparison proves all 432 padded bytes and all 21 semantic relocations exact (SHA-256 667efc085661d6b50a6fb3c778be5f3b44e05acfca0132acf3aa527b21f20b74); objdiff reports a switch-table/local-label encoding false negative"
},
{
"unit": "source/ai/path_smoothing",
"function": "_surface_is_walkable",
"reason": "hardened COFF comparison proves all 128 padded bytes and all 5 semantic relocations exact (SHA-256 a58fa4f6cf9598874c2a0d1fb60f226f65dea7f5cbc7182a682b2faefc62580f); the private name is recovered from the source-backed path-smoothing helper cluster"
},
{
"unit": "source/rasterizer/rasterizer_debug",
"function": "_rasterizer_debug_line",
"reason": "hardened COFF comparison proves all 32 padded bytes and the REL32 relocation to _rasterizer_debug_line_shaded exact (SHA-256 60f8ba4857cfb47aa739f77d4df9844fae5d61fb4292d7d92b25c28166a7a0d7)"
},
{
"unit": "source/rasterizer/rasterizer_debug",
"function": "_rasterizer_debug_triangle",
"reason": "hardened COFF comparison proves all 32 padded bytes and the REL32 relocation to _rasterizer_debug_triangle_shaded exact (SHA-256 1b0df8cc752bec5820c858223f553d6f62fc7e39bac45bbb9b209d0f980a148a)"
},
{
"unit": "source/cutscene/recorded_animation_playback",
"function": "_recorded_animation_apply_event_stream",
"reason": "hardened COFF comparison proves all 496 padded bytes and all 34 semantic relocations exact (SHA-256 4e8a5bd6f5b73b358df9e82beed6a1a08aa23ee6f84b353aa3026df37f1b441b); objdiff reports a switch-table/local-label encoding false negative"
},
{
"unit": "source/camera/camera_scripting",
"function": "_scripted_camera_update",
"reason": "hardened COFF comparison proves all 1664 padded bytes and all 82 semantic relocations exact (SHA-256 cb504e5b44213920854f97d593ab2361bda073fcb0ffef531347b0c4b8565e06); objdiff reports a switch-table/local-label encoding false negative"
},
{
"unit": "source/game/game_engine_ctf",
"function": "_ctf_engine_display_score",
"reason": "hardened COFF comparison proves all 928 padded bytes and all 78 semantic relocations exact (SHA-256 df4518d31867315a05039fdcf8679ba2476011eeb70122ac00c6b6efef13f148); objdiff reports a switch-table/local-label ownership false negative"
},
{
"unit": "source/game/game_engine_oddball",
"function": "_oddball_engine_display_score",
"reason": "hardened COFF comparison proves all 736 padded bytes and all 56 semantic relocations exact (SHA-256 6ad4051ea66ca25449fb855b7cb0076efef3e056e80fa1a6566d7dc5fc2a2e38); objdiff reports pooled-string COMDAT ownership as a false negative"
},
{
"unit": "source/interface/first_person_weapons",
"function": "_first_person_weapon_state_from_weapon_message",
"reason": "hardened COFF comparison proves all 192 padded bytes and all 19 semantic relocations exact (SHA-256 f551efe027b59b504571439a15088c0065c23383298acbcdb85aeb04f10d3947); objdiff reports a private switch-table/local-label ownership false negative"
},
{
"unit": "source/interface/first_person_weapons",
"function": "_first_person_animation_type_from_weapon_state",
"reason": "hardened COFF comparison proves all 272 padded bytes and all 25 semantic relocations exact (SHA-256 ea1838b37d9d2a3d39b80a5118d966f264e0daecbff3a47c9f41a6968006b499); objdiff reports a private switch-table/local-label ownership false negative"
},
{
"unit": "source/interface/first_person_weapons",
"function": "_first_person_weapon_set_state",
"reason": "hardened COFF comparison proves all 688 padded bytes and all 26 semantic relocations exact (SHA-256 965964af9115913e36fa06e152828b2e857e7ac59e2cc0e6fdcff29ddebcb91f); objdiff reports a private switch-table/local-label ownership false negative"
},
{
"unit": "source/interface/ui_widget_game_data_input_functions",
"function": "_mutliplayer_settings_select_list_update_displayed_items",
"reason": "hardened COFF comparison proves all 1424 padded bytes and all 73 semantic relocations exact (SHA-256 adce7f9ab32aee72ec9d542efc297df9295a839a27406848eb8467ffea97594f); objdiff reports compiler-local switch-table labels as a false negative; January's original misspelling of multiplayer in the owner name is preserved"
},
{
"unit": "source/interface/ui_widget_game_data_input_functions",
"function": "_server_list_menu_update",
"reason": "hardened COFF comparison proves all 2016 padded bytes and all 115 semantic relocations exact (SHA-256 f7a5f8c39ab8f6200fa4dfe798c533c40d76e0f8acd06935cea2d5a696c73334); objdiff reports this complete typed server-list graph as a false negative"
},
{
"unit": "source/interface/ui_widget",
"function": "_remap_sticks_for_local_player",
"reason": "hardened COFF comparison proves all 304 padded bytes and all 31 semantic relocations exact (SHA-256 963b6756ac2f3b70faf18955bad37bac6ed40bbe366baa697feccc61e7050ce8); objdiff reports a control-flow pairing false negative after the natural switch assigns the remapped icon and reaches the shared return"
}
]
-68
View File
@@ -1,68 +0,0 @@
[
{ "file_offset": 1536, "name" : ".text" },
{ "file_offset": 1926336, "name" : "D3D" },
{ "file_offset": 2005192, "name" : "D3D_RD" },
{ "file_offset": 2011016, "name" : "D3D_RW" },
{ "file_offset": 2011136, "name" : "D3D_URW" },
{ "file_offset": 2024576, "name" : "D3DX" },
{ "file_offset": 2044112, "name" : "D3DX_RD" },
{ "file_offset": 2044320, "name" : "DSOUND" },
{ "file_offset": 2092088, "name" : "DSOUND_RD" },
{ "file_offset": 2171408, "name" : "DSOUND_RW" },
{ "file_offset": 2173056, "name" : "DSOUND_URW" },
{ "file_offset": 2173248, "name" : "XNET" },
{ "file_offset": 2218120, "name" : "XNET_RD" },
{ "file_offset": 2218496, "name" : "BINK" },
{ "file_offset": 2308672, "name" : "BINK32" },
{ "file_offset": 2313408, "name" : "BINK32A" },
{ "file_offset": 2318912, "name" : "BINK16" },
{ "file_offset": 2323808, "name" : "BINK4444" },
{ "file_offset": 2329344, "name" : "BINK5551" },
{ "file_offset": 2333760, "name" : "BINK16MX" },
{ "file_offset": 2334080, "name" : "BINK16X2" },
{ "file_offset": 2335456, "name" : "BINK16M" },
{ "file_offset": 2335968, "name" : "BINK32MX" },
{ "file_offset": 2336416, "name" : "BINK32X2" },
{ "file_offset": 2337856, "name" : "BINK32M" },
{ "file_offset": 2338208, "name" : ".XPP$ClassA" },
{ "file_offset": 2338212, "name" : ".XPP$ClassHub" },
{ "file_offset": 2338216, "name" : ".XPP$ClassMU" },
{ "file_offset": 2338220, "name" : ".XPP$ClassXID" },
{ "file_offset": 2338228, "name" : ".XPP$ClassZ" },
{ "file_offset": 2338232, "name" : ".XPP$Data" },
{ "file_offset": 2338948, "name" : ".XPPCINIT" },
{ "file_offset": 2342115, "name" : ".XPPCODE" },
{ "file_offset": 2370500, "name" : ".XPPRDATA" },
{ "file_offset": 2370592, "name" : ".idata$5" },
{ "file_offset": 2371276, "name" : ".rdata" },
{ "file_offset": 2845216, "name" : ".rdata$debug" },
{ "file_offset": 2845312, "name" : ".CRT$RIA" },
{ "file_offset": 2845316, "name" : ".CRT$RII10" },
{ "file_offset": 2845320, "name" : ".CRT$RII20" },
{ "file_offset": 2845324, "name" : ".CRT$RIZ" },
{ "file_offset": 2845328, "name" : ".CRT$XCA" },
{ "file_offset": 2845332, "name" : ".CRT$XCU" },
{ "file_offset": 2845340, "name" : ".CRT$XCZ" },
{ "file_offset": 2845344, "name" : ".CRT$XIA" },
{ "file_offset": 2845348, "name" : ".CRT$XIC" },
{ "file_offset": 2845356, "name" : ".CRT$XIZ" },
{ "file_offset": 2845360, "name" : ".CRT$XPA" },
{ "file_offset": 2845364, "name" : ".CRT$XPX" },
{ "file_offset": 2845368, "name" : ".CRT$XPZ" },
{ "file_offset": 2845372, "name" : ".CRT$XTA" },
{ "file_offset": 2845376, "name" : ".CRT$XTZ" },
{ "file_offset": 2845392, "name" : ".data" },
{ "file_offset": 3251360, "name" : ".bss" },
{ "file_offset": 6401024, "name" : "DOLBY" },
{ "file_offset": 6429088, "name" : "BINKDATA" },
{ "file_offset": 6446080, "name" : ".idata$2" },
{ "file_offset": 6446120, "name" : ".idata$3" },
{ "file_offset": 6446140, "name" : ".idata$4" },
{ "file_offset": 6446824, "name" : ".idata$6" },
{ "file_offset": 6446848, "name" : ".tls" },
{ "file_offset": 6446852, "name" : ".tls$" },
{ "file_offset": 6446860, "name" : ".tls$ZZZ" },
{ "file_offset": 6446880, "name" : ".XBLD" },
{ "file_offset": 6446896, "name" : ".XBLD$A" },
{ "file_offset": 6446912, "name" : ".XBLD$V" }
]
-39
View File
@@ -1,39 +0,0 @@
[
{
"unit": "source/objects/objects",
"section": ".bss",
"reason": "January layout, PDB-public evidence, and module-local references prove three source-private object globals followed by the externally visible debug byte",
"snapshot": {
"size": 13,
"flags": 3224371328,
"relocation_count": 0,
"normalized_sha256": "dd46c3eebb1884ff3b5258c0a2fc9398e560a29e0780d4b53869b6254aa46a96",
"symbols": [
{
"name": "_object_name_list",
"value": 0,
"type": 0,
"storage": 3
},
{
"name": "_object_memory_pool",
"value": 4,
"type": 0,
"storage": 3
},
{
"name": "_object_globals",
"value": 8,
"type": 0,
"storage": 3
},
{
"name": "_debug_objects_position_velocity",
"value": 12,
"type": 0,
"storage": 2
}
]
}
}
]
-23861
View File
File diff suppressed because it is too large. Load diff
+76 -165
View File
@@ -1,70 +1,22 @@
#!/usr/bin/env python3
# root build script
# root build script: writes build.ninja for the native ports (Linux, Windows,
# Android)
import argparse
import json
from pathlib import Path
from typing import (
IO,
Any,
Callable,
Dict,
Iterable,
List,
Optional,
Set,
Tuple,
TypedDict,
Union,
cast,
)
from tools.project_x86 import (
Object,
ObjectStatus,
ProjectConfig,
SolutionConfig,
calculate_progress,
generate_build,
is_windows,
)
import io
import os
import subprocess
import sys
from pathlib import Path
from types import SimpleNamespace
from tools import ninja_syntax
from tools.android_build import android_configure_inputs, generate_android_build
from tools.linux_build import generate_linux_build, linux_configure_inputs
from tools.windows_build import generate_windows_build, windows_configure_inputs
# arguments
parser = argparse.ArgumentParser()
parser.add_argument(
"mode",
choices=["configure", "progress"],
default="configure",
help="script mode (default: configure)",
nargs="?",
)
parser.add_argument(
"--objdiff",
metavar="BINARY",
type=Path,
help="path to objdiff-cli binary (optional)",
)
parser.add_argument(
"--csplit",
metavar="BINARY",
type=Path,
help="path to csplit binary (optional)",
)
parser.add_argument(
"--ninja",
metavar="BINARY",
type=Path,
help="path to ninja binary (optional)",
)
parser.add_argument(
"--ml",
metavar="BINARY",
type=Path,
help="path to Microsoft Macro Assembler for authentic CRT .asm units",
)
parser.add_argument(
"--linux-cc",
metavar="BINARY",
@@ -73,25 +25,25 @@ parser.add_argument(
parser.add_argument(
"--compiler-launcher",
metavar="BINARY",
help="native ports: a program that runs each compile, such as ccache "
help="a program that runs each compile, such as ccache "
"(links and the Android host's few units run the compiler directly)",
)
parser.add_argument(
"--release",
action="store_true",
help="release builds of the native ports (Linux, Windows, Android): assertions are not checked",
help="release builds (Linux, Windows, Android): assertions are not checked",
)
parser.add_argument(
"--lto",
choices=["full", "thin", "off"],
default="full",
help="link-time optimisation of the native ports (Linux, Windows): full (the default: the whole game "
help="link-time optimisation (Linux, Windows): full (the default: the whole game "
"optimised as one module, the fastest code and the slowest links), thin (parallel and incremental) or off",
)
parser.add_argument(
"--portable",
action="store_true",
help="native x86 ports (Linux, Windows): code for any x86-64 processor (SSE2) rather than for this "
help="x86 builds (Linux, Windows): code for any x86-64 processor (SSE2) rather than for this "
"machine's (-march=native, the default); use it for builds that run on other computers",
)
parser.add_argument(
@@ -100,7 +52,7 @@ parser.add_argument(
const="train",
default="use",
choices=["use", "train", "off"],
help="profile-guided optimisation of the native ports: use (the default) optimises with the profiles in pgo/, "
help="profile-guided optimisation: use (the default) optimises with the profiles in pgo/, "
"train (or plain --pgo) first records this platform's profile if it is missing, by letting an instrumented "
"build play every campaign level (needs the game data in assets/ and a display; Linux and Windows), off does "
"without",
@@ -109,7 +61,7 @@ parser.add_argument(
"--pgo-profile",
metavar="PROFDATA",
type=Path,
help="native ports: profile-guided optimisation from this profile instead",
help="profile-guided optimisation from this profile instead",
)
parser.add_argument(
"--android-ndk",
@@ -121,114 +73,73 @@ parser.add_argument(
type=str,
help="clang with the arm64_32 target for the Android guest (default: clang)",
)
if not is_windows():
parser.add_argument(
"--wrapper",
metavar="BINARY",
type=Path,
help="path to wibo or wine (optional)",
)
args = parser.parse_args()
# path setup
# the settings the builds read
sln = SimpleNamespace(
build_dir=Path("build"),
linux_cc=args.linux_cc,
compiler_launcher=args.compiler_launcher,
port_release=args.release,
port_lto=args.lto,
port_portable=args.portable,
port_pgo=args.pgo,
port_pgo_profile=args.pgo_profile,
android_ndk=args.android_ndk,
android_guest_cc=args.android_guest_cc,
)
build_dir = Path("build")
config_dir = Path("config")
config_json_path = config_dir / "config.json"
project_dir = Path("projects")
tools_dir = Path("tools")
# build config json
def is_windows() -> bool:
return os.name == "nt"
class BuildConfigObject(TypedDict):
name: str
index: int
status: str
options: Dict[str, Any]
class BuildConfigProject(TypedDict):
name: str
guid: str
objects: List[BuildConfigObject]
options: Dict[str, Any]
# build.ninja
out = io.StringIO()
n = ninja_syntax.Writer(out)
n.variable("ninja_required_version", "1.3")
n.newline()
class BuildConfig(TypedDict):
name: str
guid: str
baserom: str
projects: List[BuildConfigProject]
configure_script = Path(os.path.relpath(os.path.abspath(sys.argv[0])))
n.comment("The arguments passed to configure.py, for rerunning it.")
n.variable(
"configure_args",
[f'"{arg}"' if any(ch.isspace() for ch in arg) else arg for arg in sys.argv[1:]],
)
n.variable("python", f'"{sys.executable}"')
n.newline()
build_config: BuildConfig = json.load(open(config_json_path, "r", encoding="utf-8"))
generate_linux_build(n, sln)
generate_android_build(n, sln)
generate_windows_build(n, sln)
# solution configuration
n.comment("Reconfigure on change")
n.rule(
name="configure",
command=f"$python {configure_script} $configure_args",
generator=True,
description=f"RUN {configure_script}",
)
n.build(
outputs="build.ninja",
rule="configure",
implicit=[
configure_script,
Path("tools/ninja_syntax.py"),
*linux_configure_inputs(),
*android_configure_inputs(),
*windows_configure_inputs(),
],
)
n.newline()
sln = SolutionConfig()
sln.build_dir = build_dir
sln.config_dir = config_dir
sln.project_dir = project_dir
sln.tools_dir = tools_dir
# the build for this computer, where it could be generated (the Windows
# build is left out when SDL cannot be fetched, for instance)
default = "windows" if is_windows() else "linux"
if f"\nbuild {default}: " in out.getvalue():
n.comment("Default rule: the build for this computer")
n.default(default)
sln.name = build_config["name"]
sln.guid = build_config["guid"]
sln.baserom = build_config["baserom"]
# Apply arguments
sln.objdiff_path = args.objdiff
sln.csplit_path = args.csplit
sln.ninja_path = args.ninja
sln.ml_path = args.ml
sln.linux_cc = args.linux_cc
sln.compiler_launcher = args.compiler_launcher
sln.port_release = args.release
sln.port_lto = args.lto
sln.port_portable = args.portable
sln.port_pgo = args.pgo
sln.port_pgo_profile = args.pgo_profile
sln.android_ndk = args.android_ndk
sln.android_guest_cc = args.android_guest_cc
if not is_windows():
sln.wrapper = args.wrapper
# Tool versions
sln.objdiff_tag = "v3.6.0"
sln.csplit_tag = "v0.0.2"
# v0.0.2 plus upstream's "Fix uninit phony section"; built from source on Linux
sln.csplit_source_commit = "db510e7ef9f60bb09b13a3df88c7419e9b215cde"
sln.wibo_tag = "1.0.0"
sln.uasm_tag = "v2.57r"
sln.projects = []
for build_project in build_config["projects"]:
project = ProjectConfig(**build_project.get("options", {}))
project.name = build_project["name"]
project.guid = build_project["guid"]
project.objects = []
for build_object in build_project["objects"]:
def get_object_completed(status: str) -> ObjectStatus:
if status == "MISSING":
return ObjectStatus.Missing
elif status == "Matching":
return ObjectStatus.Matching
elif status == "NonMatching":
return ObjectStatus.NonMatching
assert False, f"Invalid object status {status}"
project.objects.append(Object(get_object_completed(build_object["status"]), Path(build_object["name"]), **build_object.get("options", {})))
sln.projects.append(project)
# build file generation
if args.mode == "configure":
if sln.matching and any(
obj["status"] != "MISSING" and obj["name"].startswith("libs/d3d8/")
for project in build_config["projects"]
for obj in project["objects"]
):
subprocess.run([sys.executable, "libs/d3d8/generate_sdk_overlay.py"], check=True)
# Write build.ninja and objdiff.json
generate_build(sln)
elif args.mode == "progress":
# Print progress information
calculate_progress(sln)
else:
sys.exit("Unknown mode: " + args.mode)
with open("build.ninja", "w", encoding="utf-8") as f:
f.write(out.getvalue())
out.close()
-106
View File
@@ -1,106 +0,0 @@
# Assertions, warnings, and halts: shared API and January matching
The owner is [source/cseries/cseries.h](../source/cseries/cseries.h).
All assertions express a condition that **must be true**. A false condition
reports the diagnostic with `display_assert(..., TRUE)` and then calls
`system_exit(-1)`. Evaluate diagnostic expressions only on the failure path.
Warnings likewise report only when their condition is false, but use
`display_assert(..., FALSE)` and **continue**. Halts are unconditional: they
report with `TRUE` and call the existing `halt_and_catch_fire()` owner.
They do not take an assertion condition.
| Intent | Current supported API | Diagnostic |
| --- | --- | --- |
| Plain assertion | `assert(expression)` | Stringified expression |
| Diagnostic assertion | `dassert(expression, diagnostic)` | Supplied string expression |
| Formatted assertion | `vassert(expression, csprintf(temporary, format, ...))` | Formatted string, produced only on failure |
| Preserve original source location | `match_assert(file, line, expression)` | Stringified expression |
| Preserve location and diagnostic | `match_dassert(file, line, expression, diagnostic)` | Supplied string expression |
| Preserve location and formatted diagnostic | `match_vassert(file, line, expression, csprintf(temporary, format, ...))` | Formatted string |
| Plain warning | `warn(expression)` | Stringified expression; nonfatal |
| Diagnostic warning | `dwarn(expression, diagnostic)` | Supplied string; nonfatal |
| Formatted warning | `vwarn(expression, csprintf(temporary, format, ...))` | Failure-only formatting; nonfatal |
| Plain halt | `halt()` | No message; unconditional fatal report |
| Diagnostic halt | `dhalt(diagnostic)` | Supplied string; unconditional |
| Formatted halt | `vhalt(csprintf(temporary, format, ...))` | Formatted string; unconditional |
Warnings and halts also have `match_warn`, `match_dwarn`, `match_vwarn`,
`match_halt`, `match_dhalt`, and `match_vhalt` counterparts. Their first two
arguments are the original `file` and `line`, followed by the ordinary API's
arguments.
`vassert`/`match_vassert` already accepted a message expression before this
cleanup. The `dassert`/`dwarn`/`dhalt` names and matching counterparts are
statement-safe diagnostic convenience wrappers for the existing behavior,
not a claim that these exact wrappers were in Bungie's January source.
Existing callers remain compatible.
The pinned XDK 3911 compiler is VC7 13.00.9254.1. Its preprocessor rejects
`#define vassert(expression, format, ...)` with C2010, and does not implement
`__VA_ARGS__`. Variadic **functions** such as `csprintf` are supported. Do not
change compiler versions, add a function-call wrapper, or force inline code
merely to obtain the newer comma-separated macro syntax.
The supplied CE reference files themselves use two-argument
`vassert(expression, message)` calls: `random_math.c`'s random-seed lock
checks, server line 1248, and client line 521. These calls corroborate the
older API, but the attachments do not include its defining `cseries.h`.
See [the source reconstruction map](user_source_reconstruction_map_20260906.md).
## Units example: assert the invariant, not the error condition
The invariant is that an inventory weapon being dropped is **not** connected
to the map:
```c
match_vassert(
"c:\\halo\\SOURCE\\units\\units.c",
8505,
!TEST_FLAG(weapon->object.flags, _object_connected_to_map_bit),
csprintf(
temporary,
"a %s tried to drop a %s which was connected to the map.",
tag_get_name(unit->definition_index),
tag_get_name(weapon->definition_index)));
```
Omitting `!` reverses the behavior: it would assert for a disconnected weapon
and accept the connected weapon. Keep the two `tag_get_name` calls and the
formatting inside the assertion message expression, so successful execution
does not evaluate them.
The matching form retains January's original filename and line number. In a
`NON_MATCHING` build, `MATCH_FILE`/`MATCH_LINE` deliberately use the actual
current source location instead. For new code with no original location to
preserve, use the ordinary `assert`/`dassert`/`vassert` form.
## Limits and adoption
- Use braces around surrounding `if`/`else` branches. Some inherited macros
expand to an `if` statement; an unbraced outer `else` can bind incorrectly.
- Keep expressions free of unnecessary side effects. The condition is
evaluated once, and the diagnostic only on failure, but argument evaluation
order inside a formatted call remains the C compiler's responsibility.
- `temporary` is the existing shared 256-byte buffer and `csprintf` uses
unbounded `vsprintf`. Preserve authenticated legacy call sites; this is not
a thread-safe or size-safe general logging facility. Do not pass arbitrary
unbounded input or preformat into this buffer on the success path.
- Do not add `NDEBUG`-based removal: the current project macros do not have
that contract. Both debug and `NDEBUG` fixture builds are tested.
- Migrate one coherent owner at a time and run its strict gate plus consumer
regression checks. Do not bulk-rewrite active lanes or lose original
diagnostic text, argument types, failure polarity, or source locations.
The executable macro tests are
[tools/test_cseries_assertions.py](../tools/test_cseries_assertions.py), using
[C fixtures](../tools/fixtures/cseries_assertions.c) compiled by the actual VC7
toolchain. They check success/failure, lazy formatting, single evaluation,
exact matching/nonmatching locations, fatal flag and `system_exit(-1)` or
`halt_and_catch_fire()` dispatch, nonfatal warning continuation, and
connected-weapon polarity. They use bounded diagnostic API doubles; they do
not validate the termination implementation, `vsprintf` buffer safety, or the
game's complete error-reporting subsystem.
See also [common constants](common_constants.md) and the
[Units cleanup ledger](object_matching_logs/units_obj_assertion_cleanup_20260906.md).
-81
View File
@@ -1,81 +0,0 @@
# Campaign house rules and working cadence
User-confirmed 2026-09-07. Apply these to reconciliation and new reconstruction.
1. Put a no-argument `void` on its own line inside the parameter list.
2. Put each parameter on its own line.
3. Use explicit returns: `return;` for void functions, an appropriate returned
value for non-void functions. Do not add an invalid bare return to non-void C.
4. Use subsystem typed macros wrapping `tag_get`.
5. Use typed object-access macros instead of repeated raw-access casts.
6. Preserve January's inline schedule; do not emit a `point_from_line3d` COMDAT.
7. Give private functions authentic or descriptive project-style names, never
`code` plus an address.
8. Likewise name globals; do not add `bss` plus address identifiers.
9. Put prototypes in their genuine owners, not unrelated consumer C files.
10. Helper/math functions may use inline assembly sparingly, with evidence.
11. If easy exactness fails, retain credible fuzzy code, document it and park.
12. Prioritize small objects in substantially reconstructed families.
13. Avoid manual/forced inlining; retain ordinary helpers and natural scheduling.
14. Use open-source Marathon as a style/semantic reference, not January byte proof.
15. When no dedicated header exists, use the closest genuine associated header.
16. Use correct enum constants in switch tables.
17. Combine declaration and initialization when practical.
18. Prefer suitable `cseries.h` macros over manual bit manipulation.
19. Use project types, e.g. `real`, rather than generic replacements.
20. Reject fake matching and source implausible for the original program.
21. Reject nonsensical logic even when bytes happen to match.
22. Use `/QIfist` where required by the target/compiler evidence. It is a
per-TU option, not a blanket default or license to change unrelated flags.
Check the complete TU and affected callers; conversion instructions and
widths must agree. Existing usage is recorded in `config/config.json`.
Rule 6 has a later, narrow owner-approved exception for folded header-inline
helpers (see `docs/object_matching_logs/claude_lane_b_HANDOFF_20260920.md`,
section 3). A translation unit may emit a shared-header `__inline` COMDAT,
including `_point_from_line3d`, only when the emitted copy is byte-identical
to January's selected copy, its caller is strictly exact, the full ownership
and build sweep has zero regressions, and the selected-provider link passes.
January's per-object folded survivor is not an absolute emission boundary.
Do not hand-expand or hand-copy the helper to evade this check.
## Batch workflow
Reconcile coherent multi-object or owner/family packets, normally several
thousand bytes when available. Review source quality, headers, ABI and emitted
ownership together; run one full build, tools suite and stable regression sweep
per batch. Use existing strict gates for ordinary exact leaves. Reserve new
bespoke runtime harnesses for ambiguous behavior/ABIs, not every tiny function.
Isolate blockers without holding unrelated admissible work. Preserve donor trees.
Publish only verified source/tooling gains to canonical and the authorized
GitHub branches. Donor-relative headline totals are not additive canonical
credit. Keep meaningful exact bytes distinct from padded and fuzzy counts.
User clarification, 2026-09-07: preserve the best available house-rule-compliant
Claude implementations in canonical and GitHub even when non-exact. Document
the donor, fresh measurements, remaining differences and research pointers;
do not leave useful fuzzy work stranded in donor trees. Closeness never
justifies undefined/nonsensical logic or inappropriate Matching credit.
Any accepted loss of exactness must be explicitly recorded and subtracted,
not hidden behind the batch's gross gains or a semantic-match override.
Refresh the canonical treemap after each **10,000 additional verified reconciled
meaningful code bytes**. Starting checkpoint for this cadence is **930,828**;
the first threshold is **940,828**. Record the next threshold in the queue.
Do not start a timer or unrelated recurring automation for this work threshold.
## Quick references
- [Constants, types, flags and representations](common_constants.md)
- [Assertions, warnings and halts](assertions.md): predicates express required
invariants; diagnostics are lazy. The weapon example needs `!TEST_FLAG(...)`.
VC7 uses a formatted message expression, not modern variadic macros.
- [Supplied-source reconstruction map](user_source_reconstruction_map_20260906.md)
- [Matching methodology](matching_methodology.md)
- [Reconciliation queue](reconciliation_queue_20260907.md)
Cross-build symbols/types are evidence, not interchangeable January layouts.
Analyze supplied executables as data; do not execute them. Preserve binaries
and private symbol assets locally, outside published source batches. Inlined
source and absent debug assets cannot be recovered automatically with certainty.
@@ -1,631 +0,0 @@
# CLAUDE LANE A — AI Core Exact-Match Closeout (79,657 Remaining Halo Bytes)
You are continuing the Halo January 14, 2002 Xbox exact-match campaign. Your job is to maximize verified, source-authentic strict matches within one isolated AI-core portfolio while producing house-rule-clean fuzzy reconstructions for genuinely unresolved functions.
## 1. Frozen baseline and isolation
Use exactly this baseline:
- Commit: `6130661c9585fdb2785b3ff6bdb2ad0cfef0520c`
- Branch represented by the baseline: `jonas/exact-pilots`
Create a new isolated Git worktree and branch from that exact commit, for example:
- Worktree: `C:\halo-worktrees\claude-lane-a-ai-core-20260920`
- Branch: `claude/lane-a-ai-core-20260920`
Before doing any work:
1. Verify `git rev-parse HEAD` equals the full baseline hash.
2. Verify `git status --short` is empty.
3. Do not use a newer or older canonical checkout as the source of truth.
4. Do not mutate the canonical checkout.
5. Do not push anything.
6. Do not rebase, merge, pull, or incorporate another active lane.
7. Do not copy complete files from stale worktrees. Historical worktrees and branches are evidence sources only.
8. Keep every experiment in this lane or under `scratch/`.
9. At completion, leave the lane committed and clean.
Expected baseline sanity figures are approximately:
- Halo accepted: `7,291 / 7,574` functions
- Halo accepted meaningful bytes: `1,443,979 / 1,770,166`
- Overall accepted: `7,558` functions / `1,465,211` meaningful bytes
- Parks: `184 active / 0 stale / 0 invalid`
Recompute these figures from the baseline rather than blindly trusting them. Stop and diagnose if the baseline materially differs.
## 2. Exclusive production scope
You may edit only these 11 production C translation units:
1. `source/ai/actor_firing_position.c` — 1,113 remaining meaningful bytes
2. `source/ai/actor_looking.c` — 5,316
3. `source/ai/actor_moving.c` — 10,402
4. `source/ai/actor_perception.c` — 17,272
5. `source/ai/actors.c` — 4,308
6. `source/ai/ai.c` — 2,253
7. `source/ai/ai_communication.c` — 11,377
8. `source/ai/ai_debug.c` — 26,513
9. `source/ai/ai_script.c` — 214
10. `source/ai/encounters.c` — 745
11. `source/ai/props.c` — 144
Frozen portfolio total: **79,657 remaining Halo meaningful bytes**.
This number is the starting residual portfolio, not earned credit. Re-measure it on the frozen baseline and disclose any difference.
Do not edit any other `.c`, `.cpp`, `.h`, `.hpp`, `.inl`, or assembly source file. In particular:
- No Xbox library or vendor-library work.
- No unrelated Halo unit.
- No shared-header edits.
- No generated-source edits.
- No compiler, comparator, split-target, object, or build-tool modification.
Documentation under `docs/object_matching_logs/`, evidence under `scratch/`, and narrowly justified configuration edits described below are allowed.
If a candidate requires a header correction, do not put a surrogate prototype in the consumer C file and do not edit the header in this lane. Record:
- the genuine owner header;
- every consumer;
- the proposed declaration;
- the exact function expected to benefit;
- the baseline controls;
- a minimal deferred patch or edit description;
- the evidence needed for a later header-owner lane.
Then continue elsewhere. A header prerequisite must not stall the portfolio.
## 3. Goal and work order
The goal is not “try a few promising functions.” Work the entire allowed portfolio in descending leverage until every remaining owner has one of these dispositions:
- strict exact and admitted;
- credible improved fuzzy source retained at zero credit;
- unchanged because the current source remains best;
- blocked with a concrete, evidence-specific reopening condition.
Start with the small units to bank inexpensive closures and validate your workflow:
1. `props`
2. `ai_script`
3. `encounters`
4. `actor_firing_position`
5. `ai`
6. `actors`
Then work:
7. `actor_looking`
8. `actor_moving`
9. `ai_communication`
10. `actor_perception`
11. `ai_debug`
You may reorder the last five when primary evidence makes another sequence more efficient. Do not spend an entire campaign fighting one allocator/x87 tie while untouched structural or unwritten work remains.
An old park or rejection is not proof of impossibility. It is binding evidence about hypotheses already tested. Reopen only when you have a genuinely new witness, mechanism, compiler-context fact, source graph, type, owner, ABI, call graph, constant, or cross-build correspondence. Do not repeat rejected spelling searches under new filenames.
## 4. Required reading before editing
Read these completely before production edits:
- `docs/campaign_house_rules.md`
- `docs/common_constants.md`
- `docs/assertions.md`
- `docs/matching_methodology.md`
- `docs/campaign_throughput_lessons.md`
- `docs/user_source_reconstruction_map_20260906.md`
- `tools/campaign/README.md`
- `docs/object_matching_logs/astra_90pct_HANDOFF_20260920.md`
- `docs/object_matching_logs/astra_90pct_admission_manifest_20260920.json`
- `docs/object_matching_logs/astra_90pct_research_atlas_20260920.md`
- `docs/object_matching_logs/astra_90pct_rejected_hypotheses_20260920.md`
- `docs/object_matching_logs/astra_90pct_context_packet_20260920.md`
- `docs/object_matching_logs/astra_90pct_debug_packet_20260920.md`
- `docs/object_matching_logs/astra_90pct_canonical_reconciliation_20260920.md`
Read the relevant unit-specific evidence before assigning each unit:
### Actor looking
- `docs/object_matching_logs/actor_looking_obj_evidence_closeout_20260919.md`
- `docs/object_matching_logs/astra_90pct_actor_looking_packet_20260920.md`
- `docs/object_matching_logs/astra_90pct_research10_actor_looking_20260920.md`
- `docs/object_matching_logs/astra_90pct_research10_actor_looking_independent_20260920.md`
- `docs/object_matching_logs/astra_90pct_research14_aiming_blockage_20260920.md`
- `docs/object_matching_logs/astra_90pct_research14_aiming_blockage_independent_20260920.md`
### Actor moving
- `docs/object_matching_logs/astra_90pct_actor_moving_avoidance_packet_20260920.md`
- `docs/object_matching_logs/astra_90pct_research8_actor_moving_20260920.md`
- `docs/object_matching_logs/astra_90pct_research11_actor_move_update_20260920.md`
- `docs/object_matching_logs/astra_90pct_research11_biped_moving_20260920.md`
### Actor perception
- `docs/object_matching_logs/astra_90pct_actor_perception_packet_20260920.md`
- `docs/object_matching_logs/astra_90pct_research10_perception_refresh_20260920.md`
- `docs/object_matching_logs/astra_90pct_research12_perception_update_20260920.md`
- `docs/object_matching_logs/astra_90pct_wave2_perception_independent_review_20260920.md`
### AI communication
- `docs/object_matching_logs/astra_90pct_research10_communication_event_20260920.md`
- `docs/object_matching_logs/astra_90pct_research10_communication_independent_20260920.md`
- `docs/object_matching_logs/astra_90pct_research18_communication_probe_20260920.md`
- `docs/object_matching_logs/astra_90pct_research18_communication_private_owner_20260920.md`
- `docs/object_matching_logs/astra_90pct_communication_independent_review_20260920.md`
- `docs/object_matching_logs/astra_90pct_wave4_communication_20260920.md`
- `docs/object_matching_logs/astra_90pct_wave4_communication_independent_review_20260920.md`
### AI debug
- `docs/object_matching_logs/astra_90pct_wave2_ai_debug_20260920.md`
- `docs/object_matching_logs/astra_90pct_wave2_ai_debug_independent_review_20260920.md`
- `docs/object_matching_logs/astra_90pct_research12_ai_debug_20260920.md`
- `docs/object_matching_logs/astra_90pct_research12_ai_debug_color_independent_20260920.md`
- `docs/object_matching_logs/astra_90pct_research12_ai_debug_length_independent_20260920.md`
Also search `docs/object_matching_logs/` for every allowed object and read its newest reports plus any report named by their “read first,” “prior work,” or “reopening” sections.
## 5. Evidence sources and authority
Evidence priority is:
1. January split object code, padding, relocations, owner topology, target data, and actual executable instructions.
2. Authenticated January PDB publics, MAP/public names, strings, and image identities.
3. Original Bungie source, symbols, headers, or same-era builds when provenance is established.
4. Ghidra and DIA evidence from authenticated supplied builds.
5. Earlier/later Halo cross-builds, HCEA, the supplied 2020 symbol executable, and open-source Marathon as corroboration.
6. Historical branches and old candidates as hypotheses only.
Useful local assets include:
- `C:\Users\isabe\Downloads\Halo Combat Evolved (Jan 14, 2002 prototype)\cachebeta.xbe`
- `C:\Users\isabe\Downloads\Halo Combat Evolved (Jan 14, 2002 prototype)\cachebeta.exe`
- `C:\Users\isabe\Downloads\Halo Combat Evolved (Jan 14, 2002 prototype)\cachebeta.pdb`
- `C:\Users\isabe\Downloads\haloleak2024\halo1\h1_tags\halo_cache_symbols.exe`
- `C:\Users\isabe\Downloads\Halo - Combat Evolved (Oct 12, 2001 prototype)\Halo 2276`
- `C:\Users\isabe\Documents\Codex\2026-09-20\files-pasted-by-the-user-halo\outputs`
- the local Marathon research tree referenced by the campaign documentation.
Known evidence limits:
- The January PDB has publics/compiland information but no useful Halo locals, lexical blocks, source lines, or original headers.
- The supplied 2020 executable was built with a different compiler and contains confirmed source differences. Its RTC locals, call graphs, assertions, and structural correspondences are corroborating evidence, not automatic January source proof.
- HCEA supplies useful types, names, and line information, but it is a different platform/build.
- IDA Free 9.4 has no callable automation endpoint here. Do not stall or ask again for one.
- Ghidra and DIA are available and authorized.
- No additional local full-symbol January build or original Halo source/header archive exists. Do not waste time repeatedly searching for byte-identical copies or asking for nonexistent artifacts.
- Decompiler output is reconstructed pseudocode, never original source text.
Use multiple independent lenses whenever possible: target disassembly, relocation identity, call graph, PDB/MAP identity, cross-build raw instructions, HCEA types, historical compiler behavior, and exact sibling controls.
## 6. Evidence-backed mechanisms to prioritize
Astra already established useful, bounded source mechanisms. Use them precisely; do not generalize them beyond their evidence.
### Actor looking
The authenticated fuzzy reconstruction establishes:
- `real look_delta_deviations[2]` and the corresponding private parameter type;
- two independently evaluated priority equalities rather than copying the first Boolean result;
- the initial `succeeded = FALSE`;
- clearing free/optional facing states before installing primary facing.
The remaining barrier is a concrete stack/register lifetime cascade involving `facing_locked`, `aiming_cosine`, `primary_priority`, the secondary aim-valid result, and desired-facing pointer lifetime. Research authentic owner types, scopes, call-return use, or natural same-compiler donor patterns for this boundary. Do not repeat the already exhausted generic scope/order, stationary-copy, representation-cast, or spelling permutations.
### Actor moving
The avoidance packet authenticates array widths, typed aggregate identities, ray/fraction/point families, the transform operation, and several real control facts. The unresolved issue is the genuine January helper/include/inlining mechanism and remaining register lifetime, not a license to emit an unauthorized `point_from_line3d` owner. Seek authentic macro/helper definitions, same-compiler donor call sites, or owner/include evidence. Do not repeat the historical helper-storage-class, force-inline, short-local, clear-first, PIN/MAX, cross-product, or generic flag searches without new evidence.
### Actor perception
The authenticated fuzzy patch establishes:
- one `interesting_orphan_index`;
- repeated inner danger predicates that are redundant semantically but supported by original control-flow evidence.
It fixes the timer and tail structural regions while retaining zero credit. Remaining barriers are a precise call-return scheduling boundary and x87 ordering around squared 2D distance. Seek an authentic helper definition, call-site type context, or natural same-compiler donor. Do not repeat declaration splits, generic scope changes, header-COMDAT distance helpers, or scalar spelling searches already shown inert.
### AI communication
The existing packets authenticate much of the possibility-array structure, narrow private types, conversation ownership, and several call relationships. Reconcile the new original private-owner evidence before trying new body shapes. The event residual still has concrete relocation-count differences in actor data, datum access, race lookup, team communication, and low-priority rating paths. Use those relocation multiplicities and January call order as a semantic source-graph diagnostic. Do not count frame equality or later-build store order as exact progress.
### AI debug
The best authenticated fuzzy body restores:
- the fifth activation-marker point;
- the vision-cone zero-vector/accumulation graph;
- January’s target extent and relocation count.
Its remaining differences are allocation/scheduling and possibly still-unrecovered scalar lifetimes. Use the 94 protected-local records and direct January references as lower-bound lifetime evidence only. Do not infer complete lifetimes, lexical scopes, or 72/80-byte buffer extents from RTC records alone. Preserve the inherited genuine `point_from_line3d` ownership exactly; do not add, remove, or duplicate it casually.
### Small AI units
For the six small units, prioritize:
- unwritten or placeholder bodies;
- authentic private names from January/HCEA/MAP evidence;
- wrong scalar widths or enum types;
- missing calls and relocation multiplicity;
- genuine control-flow topology;
- incorrect owner/linkage;
- wrong constants already catalogued in `docs/common_constants.md`;
- historical best source that was never reconciled onto this baseline.
Do not assume “small residual” means an allocator tie. Inspect the raw function and relocations first.
## 7. Complete house rules
Apply all of these to every retained source change:
1. Format a no-argument parameter list with `void` on its own line inside the parentheses.
2. Put every parameter on its own line.
3. End every function, including `void` functions, with an explicit valid `return;`; non-void functions must return an appropriate value.
4. Use subsystem typed macros wrapping `tag_get` for typed tag access.
5. Use typed object-access macros rather than repeated casts after raw `object_get`.
6. Preserve January’s inline schedule. Do not introduce an unauthorized `point_from_line3d` COMDAT or alter its selected owner.
7. Name private functions authentically when evidence exists; otherwise use a credible project-style descriptive name. Never add `code_XXXXXXXX` or address-based names.
8. Name globals authentically or descriptively according to project conventions. Never add `bss_XXXXXXXX` names.
9. Put function prototypes in their genuine owner. Never hide a missing owner declaration in an unrelated consumer C file.
10. Helper and math functions may use inline assembly only sparingly and only with function-specific machine-code evidence that C cannot reproduce naturally.
11. If exact matching is not readily achievable, retain only credible source-authentic fuzzy progress, document it, give it zero exact credit, and park it with a concrete reopening condition.
12. Prefer small objects and high-confidence closures before prolonged allocator fights.
13. Avoid manual or forced inlining. Preserve ordinary helpers and natural compiler scheduling.
14. Use open-source Marathon only as a semantic/style reference, never as January byte proof.
15. If a C file lacks a dedicated header, use the closest genuine associated owner header. Since headers are outside this lane’s write scope, document the needed owner change rather than creating a local facade.
16. Use correct named enum constants in switch tables and comparisons.
17. Declare and initialize variables together when natural and consistent with genuine source lifetime.
18. Prefer established `cseries.h` macros over manual flag and bit manipulation.
19. Use project-defined types such as `real`, `boolean`, `byte`, `short`, and `long`, not generic substitutes such as `float`, `bool`, or fixed-width types unless the project itself requires them.
20. Reject fake matching and source forms that almost certainly were not present in the original.
21. Reject nonsensical logic even if it happens to byte-match.
22. Use `/QIfist` only when target/compiler evidence requires it, only for the relevant TU, and only after checking every function and caller affected by that option.
23. Use the correct `assert`, `dassert`, `vassert`, `warn`, `dwarn`, `vwarn`, `halt`, `dhalt`, and `vhalt` family contracts. Assertion predicates describe the required true invariant. Diagnostics must remain lazy. Do not manually expand one into `display_assert`/`system_exit` merely to shape bytes.
24. Preserve authentic January bugs only when direct evidence proves them. Mark them with a precise `BUG:` comment where project policy requires it. Do not invent undefined behavior, uninitialized reads, overflows, invalid aliasing, or out-of-bounds access to steer code generation.
25. A strict match does not excuse implausible source. Equality is necessary but not sufficient for admission.
26. Do not add dead locals, redundant stores, duplicate calls, empty branches, goto-next-label constructs, self-assignments, volatile reinterpretation, arbitrary union punning, raw-byte emission, linker tricks, or code-generation pragmas unless independent evidence authenticates that exact construct.
27. Do not use manual machine bytes or edit target objects.
28. Do not change the comparator, normalization rules, semantic-credit policy, or acceptance thresholds.
29. Do not trade away an inherited exact function for a larger gross gain.
30. Preserve code, data, BSS/COMMON, relocation, linkage, COMDAT, and selected-owner topology.
31. Correct fuzzy code gets zero exact credit. Never call 100% diagnostic similarity strict exact unless the strict comparator and ownership checks agree.
32. Function exactness and whole-object completion are separate claims. Do not mark an object complete until all target owners, data, selected providers, extra owners, and veto conditions have been audited.
33. Do not count external Xbox/vendor code toward this Halo lane.
34. Do not count already-exact baseline work, embedded labels, alias duplicates, documentation, configuration edits, or repeated historical work as new bytes.
35. Do not fabricate original names. A unique byte match from `namegap.py` is a proposal, not name provenance.
## 8. Parallel worker architecture
Use subagents aggressively, but keep their responsibilities disjoint.
Recommended structure:
- One worker per C file, or one worker for each small-file group with no overlapping file.
- Dedicated research workers may inspect evidence for actor looking, moving, perception, communication, and debug.
- Workers may read anything but may write only scratch candidates and reports.
- Workers must not run Ninja, edit shared configuration, commit, push, or land directly in production.
- Workers compile with `tools/campaign/gate.py` into PID-specific scratch objects.
- The orchestrator independently reviews and lands minimal patches.
- Every landed function receives a separate adversarial review by a worker that did not author it.
- Do not let multiple workers edit the same production file.
- Do not let workers change build outputs used by another worker.
Each worker brief must include:
- exact baseline commit;
- exact owned C file;
- current exact/residual/unwritten census;
- existing parks and prior rejected hypotheses;
- mandatory evidence files;
- house rules;
- required before/after gate;
- owner/data/COMMON/point guard;
- prohibition on production/config/header edits;
- exact/fuzzy accounting rules.
## 9. Baseline workflow
Before production edits:
1. Run the full baseline build:
`ninja -j4 all_source progress semantic_progress`
2. Save a stable snapshot:
`python -m tools.campaign.stable_verdicts snapshot scratch/lane-a-before.json`
3. Save copies of:
- `build/semantic_report.json`
- the progress report;
- parked-function validation output;
- object-admission audit output;
- fake-match scan output;
- per-unit `board.py` and `gate.py --all` results;
- owner/data/COMMON/point inventories for all 11 units.
4. Validate parks:
`python -m tools.parked_functions`
5. Run object-admission audit:
`python tools/audit_object_admission.py`
6. Run tests:
`python -m pytest -q tools`
7. Run a fake-match baseline scan:
`python tools/fake_match_scan.py --format json source/ai`
8. Record the exact compiler flags for all 11 TUs from `build.ninja`.
9. For each unit, inventory:
- January function owners;
- current candidate owners;
- exact/residual/unwritten status;
- meaningful and padded size;
- normalized hashes;
- relocation count and identities;
- linkage/storage class;
- parks and reopening criteria;
- target and candidate data sections;
- COMMON/BSS;
- point-helper definitions;
- target-absent candidate owners.
## 10. Function-level workflow
For each candidate:
1. Read current source and all prior reports first.
2. Confirm it is not already exact or already accepted under another identity.
3. Use `git log --all -- <file>` and `tools/campaign/branch_sweep.py` to census distinct historical source, but do not treat historical code as admitted.
4. Inspect target and candidate using:
- `tools/campaign/gate.py`
- `tools/campaign/alndiff.py`
- `tools/campaign/tinfo.py`
- `tools/campaign/relocdiff.py`
- `tools/bijection_walk.py` where allocation bijection matters
- `tools/campaign/namegap.py` only for identity proposals
- Ghidra/DIA/raw PDB or MAP evidence as relevant.
5. Diagnose semantic structure before editing:
- missing or extra calls;
- relocation multiplicity;
- parameter width;
- return type;
- enum width/value;
- aggregate identity/layout;
- branch topology;
- loop shape;
- expression order;
- source-owned repeated predicates or stores;
- helper ownership and inlining;
- local lifetime and scope;
- declaration initialization;
- frame extent;
- x87 stack order;
- calling convention and linkage.
6. Test bounded evidence-backed shapes with in-memory edits or scratch copies. Do not perform combinatorial permutation searches or random spelling sweeps.
7. Require a strict function gate for exact claims.
8. For changed units, compare the complete unit and preserve every inherited exact sibling.
9. Run the emitted-symbol point guard wherever January does not own the helper. For `ai_debug`, preserve its inherited genuine owner byte-for-byte and prevent any duplicate owner.
10. Run the fake-match scanner on every changed file and manually adjudicate every new lead.
11. Compare all codeowners, data, COMMON, runtime sections, relocations, linkage, COMDAT selection, and target-absent owners.
12. If still fuzzy, compare it against baseline. Retain it only if it is semantically more authentic and materially improves a documented structural defect. Similarity percentage alone is not enough.
13. For every rejected shape, save a concise negative result so later workers do not repeat it.
## 11. Config policy
No header changes are permitted.
Narrow configuration changes are allowed only as follows:
### `config/parked.json`
You may change entries only for functions in the 11 allowed TUs.
- Remove a park only when the function is independently verified strict exact.
- Re-baseline an improved fuzzy function only when the retained source is house-rule compliant, genuinely better supported, and its new measurements and evidence are documented.
- Do not change unrelated parks.
- Do not use a park edit to hide a regression.
- Keep fuzzy bytes at zero credit.
### `config/symbols.json`
You may edit only an entry belonging to an allowed TU and only when an original or independently authenticated name is established through January PDB/MAP, original source, or sufficiently strong private-symbol/call-graph evidence.
- A byte-equal alias or `namegap.py` proposal alone is insufficient.
- Do not rename target symbols merely to make the comparator agree.
- Land the name and corresponding source owner atomically.
- Preserve old/new file offset, evidence source, and identity reasoning in the manifest.
### `config/config.json`
A per-TU `/QIfist` or whole-object status change is allowed only with direct target/compiler evidence and complete unit validation.
- No blanket compiler-option changes.
- No unrelated object changes.
- No “Matching” status until complete owner/data/extra-owner audit passes.
- Do not add a semantic-match exception for a nonexact function.
No other configuration or policy file may change.
## 12. Wave cadence and commits
Work in coherent waves. Do not wait for exactly 10,000 bytes if a clean smaller wave is ready, but avoid one tiny commit per leaf unless isolation is necessary.
Suggested waves:
- Wave A1: small-unit closures.
- Wave A2: actor looking and moving.
- Wave A3: communication and perception.
- Wave A4: AI debug and portfolio closeout.
- Additional waves are allowed when evidence supports them.
For each wave:
1. Land minimal production/config patches.
2. Independently review every changed function.
3. Run all per-function and complete-TU gates.
4. Run full Ninja once for the coherent batch.
5. Take a stable after snapshot and diff against the frozen baseline, not merely the previous wave.
6. Recompute semantic accepted bytes.
7. Validate parks and admissions.
8. Run changed-file fake scans and owner/data/COMMON/point checks.
9. Run `git diff --check`.
10. Commit code, config, and its evidence report together.
At each cumulative 10,000 newly verified meaningful-byte checkpoint, also run the full tools test suite and record the refreshed treemap/progress inputs. Do not claim the checkpoint until the semantic accepted ledger proves it.
Never squash away the evidence boundaries. Keep an ordered, cherry-pickable commit series.
## 13. Verification and accounting
Maintain these totals separately:
1. **New strict meaningful Halo bytes** — authoritative campaign credit from the accepted semantic ledger.
2. **New strict padded bytes** — physical function-section gain from stable comparison.
3. **New strict functions**.
4. **Fuzzy meaningful bytes changed or improved** — always zero exact credit.
5. **Whole-object completions** — separate status, never duplicate function credit.
6. **Regressions** — must remain zero.
Do not report:
- diagnostic similarity as byte credit;
- target-size totals for nonexact bodies as “progress”;
- already exact functions as new;
- config-only identity changes without admission proof;
- external/vendor bytes;
- duplicate aliases or embedded labels;
- gross gains that conceal losses.
Every exact row in the final manifest must include:
- unit;
- function;
- meaningful bytes;
- padded bytes;
- target section/index;
- target and candidate normalized SHA-256;
- relocation count and identity result;
- before/after verdict;
- evidence basis;
- reviewer;
- source commit;
- owner/data/COMMON/point result.
Every fuzzy row must include:
- zero credit explicitly;
- baseline and final measurements;
- what source defect was corrected;
- why the retained code is more authentic;
- remaining differing regions;
- rejected alternatives;
- precise reopening evidence.
## 14. Final verification
On final tip:
1. Confirm `git status --short` is empty.
2. Run:
`ninja -j4 all_source progress semantic_progress`
3. Save:
`python -m tools.campaign.stable_verdicts snapshot scratch/lane-a-after.json`
4. Diff against the frozen baseline snapshot:
`python -m tools.campaign.stable_verdicts diff scratch/lane-a-before.json scratch/lane-a-after.json`
5. Run:
- `python -m tools.parked_functions`
- `python tools/audit_object_admission.py`
- `python tools/fake_match_scan.py --format json source/ai`
- `python -m pytest -q tools`
- `git diff --check 6130661c9585fdb2785b3ff6bdb2ad0cfef0520c..HEAD`
6. Verify all inherited exact functions remain exact.
7. Verify no production C/H file outside the 11-file allowlist changed.
8. Verify no Xbox/vendor source changed.
9. Verify no target object, build tool, compiler, comparator, or acceptance policy changed.
10. Verify all data, COMMON/BSS, owner, linkage, COMDAT, and point-helper inventories.
11. Verify every retained fake-scan lead is inherited or individually evidence-approved.
12. Recompute exact meaningful and padded gains from final production, not worker self-reports.
13. List the ordered commit range from baseline to tip.
14. Do not push and do not touch canonical.
## 15. Required deliverables
Create:
- `docs/object_matching_logs/claude_lane_a_ai_core_HANDOFF_20260920.md`
- `docs/object_matching_logs/claude_lane_a_ai_core_admission_manifest_20260920.json`
- `docs/object_matching_logs/claude_lane_a_ai_core_rejected_hypotheses_20260920.md`
The handoff must contain:
- worktree, branch, baseline, and final tip;
- exact ordered commits;
- complete changed-file list;
- baseline and final accepted totals;
- strict meaningful, padded, and function gains;
- fuzzy extent at zero credit;
- zero-regression proof;
- per-wave results;
- per-function evidence;
- config changes and why;
- all header prerequisites deferred;
- every remaining owner and reopening condition;
- verification commands and actual outputs;
- whether the tree is clean;
- explicit statement: nothing pushed and canonical untouched.
The JSON manifest must be machine-readable and include hashes, measurements, relocations, evidence, reviewer decisions, exact/fuzzy disposition, and credit for every retained change.
Preserve compact reproducible scratch evidence locally, but do not commit huge generated disassemblies, copied binaries, object files, or private symbol assets. Record their paths and hashes in the handoff.
## 16. Completion standard
Do not stop because one large function reaches an allocator tie. Move to the next function and continue the portfolio.
Do not declare the lane “exhausted” unless:
- every residual/unwritten owner in all 11 units was inventoried;
- previous work was checked for duplication;
- all existing high-confidence Astra mechanisms were either integrated, rejected with current evidence, or superseded;
- every new evidence-backed structural hypothesis was tested in bounded form;
- every retained change follows the house rules;
- every exact claim passed strict production verification;
- every nonexact improvement is explicitly zero credit;
- every untouched blocker has a concrete reopening condition rather than a generic “compiler tie” label.
The desired outcome is the largest defensible strict gain possible from these 79,657 remaining Halo-owned bytes, plus a materially better research boundary for everything not yet exact. Source authenticity, semantics, ownership, and zero regressions take precedence over a large but artificial headline number.
@@ -1,444 +0,0 @@
HALO JANUARY EXACT-MATCH CAMPAIGN — CLAUDE LANE B
AI ACTIONS, PATHFINDING, GAMEPLAY, PHYSICS/COLLISION, BIPEDS/UNITS/VEHICLES
MISSION
Work only from canonical commit:
6130661c9585fdb2785b3ff6bdb2ad0cfef0520c
branch: jonas/exact-pilots
Your lane contains exactly 75,377 currently non-exact meaningful Halo-owned code bytes. Reconstruct as much of this entire allocation as possible, aiming for at least +50,000 newly proven strict meaningful bytes and ultimately all 75,377. Do not manufacture matches to meet the number. If evidence is insufficient, do more research; do not declare human-written Halo code inherently unmatchable.
This is one of four concurrent, disjoint Claude lanes. Stay inside the source-unit allowlist below. Do not touch Xbox/XDK libraries, Bink/vendor code, libcmt, or any source unit assigned to another lane.
Administrative “reserved,” “do not spend,” and old lane-scope holds are lifted inside this allowlist. Scientific rejections are not automatically erased: do not repeat an exhausted spelling unless new evidence refutes its premise. Parked functions in this lane may be reopened when new evidence satisfies or invalidates their recorded criteria.
Do not push. Do not modify the canonical checkout or canonical branch. Work in a new local worktree and branch created directly from 6130661c.
REQUIRED WORKTREE
Create a clean worktree such as:
C:\halo-worktrees\claude-lane-b-ai-physics-units-20260920
branch: claude/lane-b-ai-physics-units-20260920
If either name already exists, use a unique suffixed name. Never reset, delete, clean, or repurpose an existing worktree.
Before editing, prove:
- `git rev-parse HEAD` is exactly `6130661c9585fdb2785b3ff6bdb2ad0cfef0520c`.
- `git status --short` is empty.
- The configured full build passes.
- A complete stable-verdict snapshot has been saved.
- Baseline meaningful, padded, function, object, park, and test totals have been recorded.
ALLOWED SOURCE UNITS — NO OTHERS
The values are the current meaningful residual-byte ceilings at the frozen baseline.
AI/action/path family — 20,507 bytes:
- `source/ai/action_charge.c` — 3,246
- `source/ai/action_converse.c` — 248
- `source/ai/action_obey.c` — 984
- `source/ai/action_vehicle.c` — 1,679
- `source/ai/actions.c` — 1,981
- `source/ai/actor_combat.c` — 1,529
- `source/ai/actor_type_flood.c` — 1,295
- `source/ai/actor_type_infection.c` — 3,609
- `source/ai/path.c` — 2,783
- `source/ai/path_obstacle_avoidance.c` — 1,569
- `source/ai/path_obstacles.c` — 160
- `source/ai/path_smoothing.c` — 326
- `source/ai/path_structure_bsp.c` — 1,098
Game family — 5,186 bytes:
- `source/game/aim_assist.c` — 1,173
- `source/game/game.c` — 92
- `source/game/game_engine_king.c` — 444
- `source/game/game_engine_race.c` — 215
- `source/game/player_control.c` — 1,577
- `source/game/players.c` — 1,685
Memory family — 821 bytes:
- `source/memory/stack_memory_pool.c` — 821
Physics/collision family — 25,691 bytes:
- `source/physics/breakable_surfaces.c` — 4,020
- `source/physics/collision_bsp.c` — 3,158
- `source/physics/collision_debug.c` — 4,182
- `source/physics/collisions.c` — 4,744
- `source/physics/physics.c` — 9,587
Scenario family — 380 bytes:
- `source/scenario/wind.c` — 380
Units family — 22,792 bytes:
- `source/units/biped_limp_noodle.c` — 3,443
- `source/units/bipeds.c` — 13,147
- `source/units/unit_dialogue.c` — 1,041
- `source/units/units.c` — 48
- `source/units/vehicles.c` — 5,113
The corresponding genuine owner headers may be changed only when required for one of these units and only under the header protocol below. Do not edit generic shared headers such as `cseries.h`, `real_math.h`, object/tag core headers, bitmap headers, weapon headers, or unrelated subsystem headers. If a correct reconstruction requires an out-of-lane shared-header change, preserve the source candidate in scratch, document the exact required header packet and blast radius, and continue elsewhere.
The only permitted non-source edits are:
- lane-specific entries in `config/parked.json`;
- independently authenticated lane-specific names in `config/symbols.json`;
- lane documentation under `docs/object_matching_logs/`.
Do not edit build flags, tools, `semantic_matches.json`, semantic overrides, object-admission overrides, or unrelated configuration to make a candidate appear accepted.
READ THESE BEFORE EDITING
Read these files completely:
- `docs/campaign_house_rules.md`
- `docs/campaign_throughput_lessons.md`
- `docs/common_constants.md`
- `docs/assertions.md`
- `docs/matching_methodology.md`
- `docs/exact_match_acceleration_playbook.md`
- `docs/object_matching_logs/opus5_next150_compiler_laws_20260916.md`
- `docs/object_matching_logs/opus5_next150_compiler_laws_round2_20260916.md`
- `docs/object_matching_logs/astra_90pct_canonical_reconciliation_20260920.md`
- `docs/object_matching_logs/astra_90pct_HANDOFF_20260920.md`
- `docs/object_matching_logs/astra_90pct_research_atlas_20260920.md`
- `docs/object_matching_logs/astra_90pct_rejected_hypotheses_20260920.md`
- `docs/object_matching_logs/astra_90pct_admission_manifest_20260920.json`
Then read the relevant Astra evidence before touching its corresponding family:
- action charge:
- `astra_90pct_wave13_action_charge_setup_20260920.md`
- `astra_90pct_wave13_action_charge_primary_independent_20260920.md`
- `astra_90pct_wave13_action_charge_independent_20260920.md`
- `astra_90pct_wave13_call_sequence_census_20260920.md`
- `astra_90pct_research8_action_charge_20260920.md`
- `astra_90pct_research8_action_charge_independent_20260920.md`
- aim assist:
- `astra_90pct_research8_aim_assist_20260920.md`
- `astra_90pct_research8_aim_assist_independent_20260920.md`
- infection:
- `astra_90pct_research7_infection_20260920.md`
- collisions:
- `astra_90pct_wave2_collisions_20260920.md`
- physics:
- `astra_90pct_wave4_physics_20260920.md`
- `astra_90pct_wave4_physics_independent_review_20260920.md`
- `astra_90pct_wave4_frame_statistics_20260920.md`
- collision debug:
- `astra_90pct_wave6_collision_debug_20260920.md`
- bipeds:
- `astra_90pct_wave3_bipeds_packet_20260920.md`
- `astra_90pct_wave3_bipeds_independent_review_20260920.md`
- `astra_90pct_research18_bipeds_probe_20260920.md`
- `astra_90pct_research18_biped_private_owner_20260920.md`
- `astra_90pct_research11_biped_moving_20260920.md`
- vehicles:
- `astra_90pct_research7_vehicles_20260920.md`
- `astra_90pct_vehicle_independent_review_20260920.md`
- path/obstacles:
- `astra_90pct_wave17_obstacle_owner_20260920.md`
- players:
- `astra_90pct_wave17_player_probe_20260920.md`
- `astra_90pct_wave17_player_private_owner_20260920.md`
- `astra_90pct_wave17_player_independent_20260920.md`
- `astra_90pct_research16_player_context_20260920.md`
The complete immutable Astra evidence package is here:
`C:\Users\isabe\Documents\Codex\2026-09-20\files-pasted-by-the-user-halo\outputs`
Its `SHA256SUMS.json` and package README define its integrity and contents. The large function atlas is research evidence, not current accounting authority. The current build at 6130661c is authoritative.
IMPORTANT ASTRA BOUNDARY
Astra already made `_action_charge_setup` and `_player_examine_nearby_unit` strict exact. They and every other exact baseline function are immutable. Do not “improve,” restyle, or rewrite an exact function.
Use Astra’s rejected-hypothesis ledger to avoid rerunning identical failed spellings. A negative result is not a permanent ban when you have genuinely new evidence, but state what new evidence invalidates the old premise before spending on it. “Try it again” is not new evidence.
EVIDENCE SOURCES
January is the byte authority:
`C:\Users\isabe\Downloads\Halo Combat Evolved (Jan 14, 2002 prototype)`
This directory contains the January `cachebeta` XBE variants, `cachebeta.exe`, and `cachebeta.pdb`. The PDB has useful public symbols but lacks Halo local-variable and line records. Do not claim those missing records exist.
Additional evidence, never interchangeable byte targets:
- October 2001 prototype:
`C:\Users\isabe\Downloads\Halo - Combat Evolved (Oct 12, 2001 prototype)\Halo 2276`
- supplied later symbol executables and three original Bungie source files:
`C:\Users\isabe\Downloads\haloleak2024\halo1`
- open-source Marathon reference:
`C:\Users\isabe\Documents\Codex\2026-07-13\i-w\_research-marathon-source-agent-20260904`
- locally mirrored public Halo research:
- `C:\Users\isabe\Documents\Codex\2026-07-13\i-w\_research-punpck-halo`
- `C:\Users\isabe\Documents\Codex\2026-07-13\i-w\_research-stian-halo`
- the other `_research-*` trees beside them.
The supplied 2020 executables have confirmed source differences. They may establish names, types, broad control-flow topology, ownership, and semantic intent, but their instructions are not January byte proof.
Use Ghidra and DIA where useful. Use the installed IDA Free interactively for ambiguous high-value functions when available, but it has no working callable scripting/RPC interface; do not waste the campaign repeatedly probing for one or declare the lane blocked because IDA cannot be automated. Analyze supplied binaries as data only—never execute them.
HOUSE RULES — ALL ARE BINDING
1. Format a no-argument function with `void` on its own line inside the parameter list.
2. Put every parameter on its own line.
3. End every function with an explicit return: `return;` for `void`, an appropriate value for non-void functions.
4. Use subsystem typed macros wrapping `tag_get`; do not scatter raw typed casts.
5. Use typed object-access macros instead of repeated casts after raw `object_get`.
6. Preserve January’s inline schedule and do not emit a `point_from_line3d` COMDAT.
7. Give private functions authentic or descriptive project-style names; never use `code_XXXXXXXX` or address names.
8. Give globals authentic or descriptive project-style names; never use `bss_XXXXXXXX` or address names.
9. Put prototypes in their genuine owner locations, not unrelated consumer `.c` files.
10. Inline assembly is exceptional. It may be used sparingly in a genuine helper/math routine only when target evidence proves ordinary VC7 C cannot emit the required instructions. Earlier asm rulings are function-specific and do not create blanket permission.
11. If easy exactness fails, preserve credible, useful fuzzy source, document it, and park it at zero credit.
12. Prioritize small objects and small unwritten functions within these substantially reconstructed families.
13. Avoid manual or forced inlining. Reproduce the natural helper boundary and January schedule.
14. Use Marathon as a style/semantic reference, not as January byte proof.
15. If a `.c` file has no dedicated header, use the closest genuine associated header.
16. Use correct enum constants in switch tables; do not replace missing research with raw magic case numbers.
17. Combine declaration and initialization when practical and source-authentic.
18. Prefer appropriate `cseries.h` macros over manual bitwise logic.
19. Use project-defined types such as `real`, not generic substitutes such as `float`.
20. Reject fake matching and source Bungie almost certainly would not have written.
21. Reject nonsensical logic even when it coincidentally byte-matches.
22. Use `/QIfist` only where target/compiler evidence requires it. It is a per-TU property, not a blanket flag. Audit the complete TU and callers before changing it.
23. Assertions/warnings/halts must follow `docs/assertions.md`. Predicates state required invariants, diagnostics remain lazy, and the weapon-connected example requires `!TEST_FLAG(...)`. Do not hand-expand `display_assert` when the genuine `assert`/`dassert`/`vassert` family is appropriate.
24. Never invent undefined behavior, uninitialized reads, out-of-bounds access, representation punning, volatile steering, dummy locals, redundant assignments, self-assignments, impossible conditions, raw-byte emission, pragmas, compiler barriers, fake globals, or arbitrary struct offsets to force bytes.
25. If January conclusively proves an original bug that cannot be expressed in defined natural C, preserve the evidence and candidate at zero credit and request a narrow owner ruling. Do not silently land UB.
26. Correct semantics, ABI, linkage, symbol ownership, data ownership, and source authenticity are required in addition to byte equality.
STRICT CREDIT
A function receives exact credit only when the current strict comparator proves the January and candidate sections identical, including size, normalized bytes, relocation layout and identities, and the required section/owner metadata.
Do not count:
- fuzzy or parked functions;
- “100%” fuzzy similarity;
- same-size functions;
- matching relocation counts without matching instructions;
- semantic overrides;
- asm-implemented or externally supplied rows;
- padded alignment bytes as meaningful bytes;
- pre-existing exact functions;
- worker estimates;
- renamed functions without independent name provenance;
- whole objects whose data/BSS/linkage/extra-owner state has not been audited.
Keep these totals separate in every report:
1. newly strict exact functions;
2. newly strict meaningful code bytes from the accepted ledger;
3. newly strict padded bytes from stable-verdict diff;
4. credible fuzzy bytes retained at zero credit;
5. object-level completions after complete code/data/ownership audit.
Do not mark an object green/Matching merely because all visible functions look exact.
ANTI-DUPLICATION AND CURRENT-STATE INVENTORY
Before source work:
1. Run the current board on every allowed unit.
2. Record each target function’s target-section identity, baseline status, meaningful/padded size, relocation count, frame size, and current owner.
3. Query the Astra function atlas, handoff, and rejection ledger by function.
4. Run `tools/campaign/branch_sweep.py` for each allowed source file to locate better historical bodies without blindly cherry-picking them.
5. Map promising blobs back to commits and read their ledgers/reviews.
6. Treat historical code only as a donor hypothesis. Recompile against 6130661c and re-audit it under current house rules.
7. Never assign two workers the same `.c` file.
8. Never count a function already exact at baseline as a lane gain.
Use the current source/config trees, not an old patch, old all-lanes treemap union, or a prior worker’s headline total.
THROUGHPUT STRATEGY
Do not spend the campaign fighting one register-allocation tie while unwritten or structurally wrong functions remain.
Start with a closeout wave over the smallest residual objects:
- `units` — 48
- `game` — 92
- `path_obstacles` — 160
- `game_engine_race` — 215
- `action_converse` — 248
- `path_smoothing` — 326
- `wind` — 380
- `game_engine_king` — 444
- `stack_memory_pool` — 821
- `action_obey` — 984
Within every unit, prioritize:
1. unwritten bodies;
2. proven missing calls/helper boundaries/incorrect signatures or types;
3. structural control-flow and frame-layout defects;
4. exact historical donors that pass current authenticity review;
5. only then near-tie scheduling/x87/register residuals.
After a few bounded source-shape attempts without new evidence, move to another function. Do not create a bespoke Python tester for every 200–500 byte function. Use the universal gate, relocation diff, aligned disassembly, and stable sweep. Add a runtime harness only when behavior or ABI is genuinely ambiguous.
Return to hard functions after a research pass across:
- public/private symbol provenance;
- caller and callee graphs;
- strings/assert paths/line-number evidence;
- target relocation identities;
- cross-build topology;
- analogous exact functions in January;
- C2 compiler probes;
- Ghidra/IDA/DIA evidence;
- original Bungie/Marathon coding idioms.
A historical park is a research index, not an excuse to stop. Test its explicit reopen criterion. If the criterion is stale or contradicted, reopen it honestly and update only that lane-specific park entry.
PARALLEL AGENT ARCHITECTURE
Use an orchestrator plus workers and independent adversarial reviewers.
- One worker per `.c` file; files must remain strictly disjoint.
- Workers use `tools/campaign/gate.py` and scratch objects only.
- Workers do not run `ninja`, `configure.py`, full pytest, or shared-build commands.
- Workers do not edit headers, config, tools, or commit.
- Workers report exact baseline counts and only newly gained functions.
- The orchestrator reviews and applies candidates to the real source.
- A different agent independently reviews every proposed landing for semantics, house rules, ownership, and exactness.
- The orchestrator alone performs full builds, config updates, and commits.
`gate.py` is safe for concurrent TU work because it writes PID-isolated scratch objects. Useful forms include:
- `python tools/campaign/gate.py <unit>`
- `python tools/campaign/gate.py <unit> --fn <function>`
- `python tools/campaign/gate.py <unit> --source <scratch-source>`
- `python tools/campaign/gate.py <unit> --edits <json>`
- `python tools/campaign/gate.py <unit> --out <scratch-object>`
- `python tools/campaign/gate.py <unit> --forbid-emitted-symbol _point_from_line3d --all`
Do not rely on `gate.py --disas` after instruction counts diverge; it is index-zipped. Use:
- `python tools/campaign/alndiff.py <unit> <function>`
- `python tools/campaign/relocdiff.py <unit> <function>`
- `python tools/campaign/tinfo.py <unit> --fn <function>`
- `python tools/bijection_walk.py --unit <unit> <function> -v`
Use spelling sweeps only to test evidence-backed hypotheses. A successful brute-force spelling is not authenticity evidence by itself.
HEADER PROTOCOL
Header edits are high risk because VC7 allocation can change in unrelated includers based only on declaration position.
Before touching a header:
1. Enumerate every including `.c` with `rg`.
2. Prove the declaration belongs in that header.
3. Confirm it cannot correctly remain TU-private in the owner `.c`.
4. Save a pristine baseline snapshot.
5. Make the header change as a separate prerequisite commit.
6. Rebuild and compare every dependent TU against pristine.
7. Run the whole-tree stable sweep.
8. Reject the header change if it loses any inherited exact function, even if the lane’s net total rises.
9. Never compensate for a header regression with unrelated source steering.
Private structs, enums, helpers, and prototypes should remain in their genuine owner TU when historically and semantically appropriate. Public prototypes belong in the real subsystem header, never copied into whichever `.c` happens to call them.
CONFIG AND NAMING POLICY
`config/parked.json` may change only for functions in this allowlist:
- remove an entry after current strict proof;
- remeasure it when an admitted source/header change legitimately changes the residual;
- add a credible fuzzy landing with measured remaining differences and a concrete reopen criterion.
`config/symbols.json` may change only when the name has independent evidence from a January public, PDB, source/map artifact, unambiguous cross-build identity plus caller graph, or similarly strong provenance. A unique byte alias is a proposal, not name proof.
Do not retain `code + address` or `bss + address` identifiers in finished source. If the original private name is unrecoverable, use a descriptive project-convention name, label it as descriptive rather than proven, and document the evidence.
FULL VERIFICATION
At baseline:
- full configured `ninja -j4 all_source progress semantic_progress`;
- `python -m tools.campaign.stable_verdicts snapshot scratch/lane_b_before.json`;
- `python -m tools.campaign.board`;
- park validation;
- object-admission audit;
- test suite;
- changed-scope fake-scan baseline.
After each coherent wave—and at least every +10,000 newly proven meaningful bytes—the orchestrator must:
1. run all affected per-function strict gates;
2. run the `_point_from_line3d` emitted-symbol guard on every changed TU;
3. run a full `ninja -j4 all_source progress semantic_progress`;
4. capture a new stable-verdict snapshot;
5. diff it against the frozen lane baseline and prove zero exact regressions;
6. inspect accepted meaningful totals from the semantic report;
7. validate parks with zero stale/invalid entries;
8. run the object-admission audit;
9. run `tools/fake_match_scan.py` over every changed source file and adjudicate every new finding;
10. audit emitted code/data/COMMON owners;
11. run relevant runtime/ABI tests and the full pytest suite at final;
12. run `git diff --check`;
13. review the complete diff against the source allowlist.
No inherited exact regression is permitted. Net gain does not excuse one lost exact function.
COMMITS
Commit in coherent, reviewable waves, preferably family/file packets. Keep prerequisite header changes separate. Keep zero-credit fuzzy preservation separate from strict admissions. Do not create a giant opaque final commit.
Every commit message and wave record must identify:
- affected units/functions;
- new strict meaningful and padded bytes;
- fuzzy bytes, separately;
- stable regression result;
- config/header changes;
- evidence used;
- independent reviewer result.
Do not push. Do not merge into `jonas/exact-pilots`. Leave a clean local lane.
FINAL DELIVERABLES
Create:
- `docs/object_matching_logs/claude_lane_b_HANDOFF_20260920.md`
- `docs/object_matching_logs/claude_lane_b_admission_manifest_20260920.json`
- `docs/object_matching_logs/claude_lane_b_rejected_hypotheses_20260920.md`
The handoff must include:
- worktree, branch, frozen base, and final tip;
- ordered commit list;
- exact changed-file list;
- baseline and final accepted ledgers;
- newly strict functions with unit, target section, meaningful bytes, padded bytes, relocation counts, and commit;
- fuzzy functions retained at zero credit with fresh measurements;
- all header/config edits and their blast-radius proofs;
- exact-regression proof;
- test/build/park/admission/fake-scan results;
- remaining function-by-function inventory;
- concrete next evidence required for every remaining substantial function;
- any candidates held for a narrow owner ruling;
- confirmation that no out-of-lane source, vendor library, canonical checkout, or remote branch was touched.
The JSON manifest must be machine-readable and include baseline/final hashes, per-function evidence, source provenance, strict verdict, semantic-credit status, reviewer decision, and commit hash.
If context or token budget becomes low, stop launching new probes. Finish the current coherent packet, run the complete verification, commit it, write the handoff/manifest, and leave the tree clean. Do not leave valuable work uncommitted or source claims unsupported.
Do not stop merely because the headline +50,000 target looks difficult. Continue broad, evidence-driven work across the full 75,377-byte allocation. The valid stopping conditions are:
- every allocated byte is strict exact; or
- every remaining row has received a fresh evidence pass, all admissible improvements are committed, and each unresolved function has a specific documented blocker/reopen criterion rather than a generic “compiler tie” claim.
@@ -1,564 +0,0 @@
You are Claude Opus 5 running Lane C of the Halo: Combat Evolved January 14, 2002 exact-match campaign.
Your task is to reconstruct and strictly verify the remaining Halo-owned graphics/UI code assigned below. Work for maximum durable progress, not a brief sampling pass.
## 1. Frozen baseline and isolation
The immutable starting point is:
- Branch: `jonas/exact-pilots`
- Commit: `6130661c9585fdb2785b3ff6bdb2ad0cfef0520c`
- Canonical checkout, READ ONLY:
`C:\Users\isabe\Documents\Codex\2026-07-13\i-w\worktrees\astra-reconcile-20260920`
Use a dedicated worktree and branch, for example:
- Worktree: `C:\halo-worktrees\claude-lane-c-graphics-ui-20260920`
- Branch: `claude/lane-c-graphics-ui-20260920`
If that worktree or branch already exists, inspect it instead of blindly recreating it. Before doing any work, prove:
```text
git status --short
git rev-parse HEAD
```
The tree must be clean and HEAD must be exactly `6130661c9585fdb2785b3ff6bdb2ad0cfef0520c`.
Never edit the canonical checkout, never move or reset `jonas/exact-pilots`, and never push. Make local commits only in your dedicated lane. Do not cherry-pick another lane wholesale.
## 2. Objective
This lane begins with approximately **83,898 meaningful non-exact Halo code bytes** in its assigned units.
Campaign target:
- Primary target: at least **50,000 newly proven strict meaningful exact bytes**.
- Stretch target: close the entire **83,898-byte** assigned residual pool.
- Also maximize genuinely complete, whole-object `Matching` closures.
- Preserve credible, house-rule-compliant fuzzy improvements at zero matching credit when exactness is not yet attainable.
Do not stop after one or two low-yield waves. Do not declare the target impossible merely by summing current park entries. Parks are prior evidence and hypotheses, not eternal prohibitions; reopen them only when their recorded criteria or genuinely new evidence justify it.
A lower result is acceptable only after every in-scope residual has received a current-baseline classification and all high-confidence structural opportunities have been pursued. “Register allocation tie” or “compiler noise” requires evidence from the existing classifiers or controlled experiments, not intuition.
Count only gains relative to commit `6130661c`. Pre-existing exact functions, donor-relative gains, fuzzy bodies, helper COMDATs, and already-integrated Astra work are not new credit.
## 3. Exclusive source scope
Only the following Halo translation units are assigned to this lane. The byte figures are starting estimates and must be remeasured at the frozen baseline.
### Bitmaps — 8,790 bytes
```text
source/bitmaps/bitmap_group.c 995
source/bitmaps/bitmap_utilities.c 2,362
source/bitmaps/bitmaps.c 1,287
source/bitmaps/s3tc/s3tc.c 4,146
```
### Interface/UI — 32,470 bytes
```text
source/interface/first_person_weapons.c 1,533
source/interface/hud.c 1,744
source/interface/hud_draw.c 364
source/interface/hud_messaging.c 2,922
source/interface/hud_nav_points.c 1,618
source/interface/hud_weapon.c 7,696
source/interface/interface.c 2,981
source/interface/motion_sensor.c 3,144
source/interface/progress_bar.c 349
source/interface/ui_widget.c 6,425
source/interface/ui_widget_game_data_input_functions.c 1,008
source/interface/virtual_keyboard.c 2,686
```
### Rasterizer — 34,352 bytes
```text
source/rasterizer/rasterizer_frame_statistics.c 4,236
source/rasterizer/rasterizer_geometry.c 1,179
source/rasterizer/rasterizer_lights.c 3,049
source/rasterizer/xbox/rasterizer_xbox_active_camouflage.c 2,014
source/rasterizer/xbox/rasterizer_xbox_environment.c 4,957
source/rasterizer/xbox/rasterizer_xbox_environment_fog.c 4,685
source/rasterizer/xbox/rasterizer_xbox_hardware_bitmaps.c 388
source/rasterizer/xbox/rasterizer_xbox_hardware_geometry.c 337
source/rasterizer/xbox/rasterizer_xbox_lights.c 2,338
source/rasterizer/xbox/rasterizer_xbox_models.c 9,553
source/rasterizer/xbox/rasterizer_xbox_shadows.c 1,616
```
### Render — 8,286 bytes
```text
source/render/render_cameras.c 4,649
source/render/render_particles.c 1,154
source/render/render_sprite.c 2,483
```
The arithmetic total for the complete allowlist is **83,898 meaningful residual bytes**.
The files under `source/rasterizer/xbox/` listed above are Halo-owned Xbox renderer code and are explicitly in scope. Do not classify them as external merely because `xbox` appears in the path.
Everything else is out of scope:
- No Xbox SDK/XDK library objects.
- No Bink, Direct3D, XAPI, libcmt, zlib, libtiff, or other vendor-library campaign work.
- No AI, units, vehicles, networking, physics, effects, sound, game-state, or unrelated renderer files.
- Do not modify another lane’s `.c` file to make one of your objects match.
Related genuine headers and narrowly necessary configuration entries may be changed only under the header/configuration policy below.
## 4. Required reading before assigning work
Read these files completely before editing:
```text
docs/campaign_house_rules.md
docs/matching_methodology.md
docs/exact_match_acceleration_playbook.md
docs/campaign_throughput_lessons.md
docs/common_constants.md
docs/assertions.md
docs/user_source_reconstruction_map_20260906.md
docs/fake_match_review.md
tools/campaign/README.md
docs/object_matching_logs/opus5_next150_compiler_laws_20260916.md
docs/object_matching_logs/opus5_next150_compiler_laws_round2_20260916.md
docs/object_matching_logs/opus5_next150_research_campaign_20260916.md
```
Then search and read every relevant current ledger before touching a function:
```text
docs/object_matching_logs/astra_90pct_*
docs/object_matching_logs/*bitmap*
docs/object_matching_logs/*hud*
docs/object_matching_logs/*interface*
docs/object_matching_logs/*widget*
docs/object_matching_logs/*motion_sensor*
docs/object_matching_logs/*rasterizer*
docs/object_matching_logs/*render_camera*
docs/object_matching_logs/*render_particle*
docs/object_matching_logs/*render_sprite*
config/parked.json
config/symbols.json
```
Use `rg` by exact unit and function name. Do not rely on filenames alone. Newer Astra ledgers supersede older guesses when they contain fresh measurements, but every candidate must still be remeasured against commit `6130661c`.
Before assigning a function to a worker, run the existing unclassified/ledger cross-reference workflow described in the compiler-law documents. Determine:
1. Is it already exact at this baseline?
2. Is the same source already integrated under another name?
3. Was the proposed shape already tried?
4. Is it parked, and what is the explicit reopen criterion?
5. Is there a historical branch with a stronger body?
6. Is the mismatch structural, relocation/ownership, frame/type, helper-boundary, x87 ordinal, or a proven allocator/scheduling tie?
Do not duplicate prior work merely because an old handoff started from a lower byte total.
## 5. Evidence hierarchy and research assets
Use evidence in this order:
1. January target object bytes, relocations, section ownership, public symbols, strings, callers, and callees.
2. Authentic Bungie source or authenticated same-function cross-build source.
3. Earlier/later Halo builds for semantics, names, types, and control-flow hypotheses.
4. Open-source Marathon for Bungie style and likely abstractions.
5. Compiler micro-labs and corpus-wide laws for source-shape discrimination.
6. Pure visual similarity or decompiler output only as a hypothesis.
Relevant local evidence includes:
```text
C:\Users\isabe\Downloads\Halo Combat Evolved (Jan 14, 2002 prototype)
C:\Users\isabe\Downloads\Halo - Combat Evolved (Oct 12, 2001 prototype)\Halo 2276
C:\Users\isabe\Downloads\haloleak2024\halo1
C:\Users\isabe\Downloads\haloleak2024\halo1\h1_tags
C:\Users\isabe\Documents\Codex\2026-07-13\i-w\_research-marathon-source-agent-20260904
C:\Users\isabe\Documents\Codex\2026-07-13\i-w\_research-punpck-halo
C:\Users\isabe\Documents\Codex\2026-07-13\i-w\_research-stian-halo
C:\Users\isabe\Documents\Codex\2026-07-13\i-w\_research-halopc-restored
C:\Users\isabe\Documents\Codex\2026-07-13\i-w\_research-demon
C:\Users\isabe\Documents\Codex\2026-07-13\i-w\halocea-review
C:\Users\isabe\Documents\Codex\2026-07-13\i-w\research
C:\Users\isabe\Documents\Codex\2026-07-13\i-w\research-cache
C:\Users\isabe\Documents\Codex\2026-07-13\i-w\_research_cache
```
Known limitations:
- The January `cachebeta.pdb` has useful public symbols but lacks Halo locals and line records.
- The supplied 2020 symbol executables have confirmed source differences. Use them for names, types, and semantic hypotheses—not January byte proof.
- Use both IDA and Ghidra evidence where available. IDA Free on this machine has no usable headless scripting/RPC interface; do not burn a wave repeatedly trying to automate it. Use an existing interactive IDA database/export if available, and use Ghidra headless plus DIA/public-symbol tooling for repeatable extraction.
- Never execute supplied game/tool binaries merely to inspect them.
- Decompiler pseudocode is not original source.
When existing evidence is insufficient, research the target rather than declaring human-written code impossible. Build caller/callee graphs, inspect data and relocation ownership, compare cross-build implementations, search authentic source idioms, and construct narrowly controlled compiler labs. Validate any new “compiler law” against exact controls before applying it.
## 6. House rules — mandatory
1. Format a no-argument parameter list with `void` on its own line inside the parentheses.
2. Put every parameter on its own line.
3. End every function—including `void` functions—with an explicit `return;`. Non-void functions must return an appropriate value, never a bare `return;`.
4. Use subsystem macros wrapping `tag_get` for typed tag access.
5. Use typed object-access macros rather than repeated casts after raw `object_get` calls.
6. Preserve the January inline schedule without emitting a `point_from_line3d` COMDAT.
7. Name private functions correctly. Never introduce `code_XXXXXXXX`, `sub_XXXXXXXX`, or address-based private names.
8. Name globals correctly. Never introduce `bss_XXXXXXXX` or address-based names. When the original name is unavailable, use a descriptive name consistent with project conventions and label it as inferred.
9. Put function prototypes in their genuine owner headers or genuine TU-private location, never in an unrelated consumer `.c` file.
10. Helper and math functions may use inline assembly only sparingly and with direct evidence. Existing admitted asm is not blanket precedent for new asm.
11. If easy exactness is not achievable, retain the best credible fuzzy reconstruction, document and park it, and grant it zero exact credit.
12. Prioritize small functions and near-complete objects within this assigned family, while still researching the larger structural functions.
13. Avoid forced/manual inlining. Preserve natural helpers and January’s observed inline schedule.
14. Use open-source Marathon as a style and semantic reference, not proof of January bytes.
15. If a `.c` file has no dedicated header, use the closest genuine associated header.
16. Use proper enum constants in switch tables, not unexplained raw case numbers.
17. Declare and initialize variables together when practical and natural.
18. Prefer established `cseries.h` macros over manual bit manipulation.
19. Use project-defined types such as `real`, `boolean`, `byte`, `word`, and `dword` rather than generic substitutes such as `float`, C++ `bool`, or platform-dependent integers.
20. Reject fake matching and source that Bungie almost certainly would not have written.
21. Reject nonsensical function logic even if it coincidentally byte-matches.
22. Use `/QIfist` only where target/compiler evidence requires it. It is a per-TU option, not a blanket setting.
Additional fidelity requirements:
- Use `assert(expression)`, `dassert(expression, diagnostic)`, and `vassert(expression, format, ...)` through the established `cseries.h` owner. Likewise use the established `warn`/`dwarn`/`vwarn` and `halt`/`dhalt`/`vhalt` families. Do not hand-expand them into `display_assert`, formatting buffers, and `system_exit`.
- Assertion predicates state the required invariant. Do not accidentally invert them.
- Preserve evaluation count, evaluation order, and lazy diagnostics.
- No `volatile`, `register`, dummy statements, dead locals, arbitrary buffer inflation, invented globals, compiler barriers, pragmas, raw byte emission, self-assignments, empty branches, goto-next-label tricks, or duplicated equivalent branches merely to steer code generation.
- No undefined behavior, uninitialized reads, out-of-bounds accesses, inactive-union-member tricks, representation-punning casts, raw numeric struct offsets, or stack overruns for a match. If January appears to contain a genuine bug, document the evidence and hold it for an owner ruling; do not silently land it.
- Do not extend function-specific prior rulings to other functions.
- Correct semantics and credible source are admission requirements even when bytes match.
## 7. Strict exactness, fuzzy work, and accounting
A function is strict exact only when the established comparator proves:
- exact function size,
- exact normalized code bytes,
- exact relocation count,
- exact relocation addresses/types,
- exact relocation target identities,
- and the correct target-function owner.
Do not call a size/relocation near-tie exact. Do not use semantic overrides or matching allowlists to manufacture credit.
Maintain three separate totals:
1. Newly strict **meaningful** code bytes from `build/semantic_report.json`.
2. Newly strict padded/function-section bytes from the stable verdict diff.
3. Credible fuzzy bytes retained at **zero credit**.
Also report:
- strict functions gained,
- strict functions lost,
- newly complete function-only objects,
- newly admitted whole `Matching` objects,
- data/BSS/linkage/extra-owner changes,
- parks removed, remeasured, stale, or invalid.
A function-only 100% object is not automatically a whole matching object. Before marking an object `Matching`, prove:
- every required Halo function is strict exact,
- data, rdata, BSS, and COMMON ownership agree,
- linkage/storage classes agree,
- no extra code or data owner is emitted,
- helper/COMDAT ownership agrees with January,
- the object is source-authentic,
- and the object-admission audit accepts it.
A blue “100%” result is not green whole-object proof. Halo-owned Xbox rasterizer objects must not be hidden as `external`.
Never trade away an inherited exact function. Gross gain does not conceal a regression. If an intentional exactness loss is ever proposed, do not land it without an explicit owner ruling and explicit subtraction from the totals.
## 8. Anti-duplication procedure
Before editing each unit:
```text
git log --all -- <unit.c>
python tools/campaign/branch_sweep.py <unit-without-.c>
python tools/campaign/gate.py <unit-without-.c> --all
python -m tools.campaign.unparked <unit-without-.c>
```
Search the function and unit name across all ledgers and handoffs. Map any promising historical blob back to its commits and provenance. A historical exact body still needs current-baseline source, owner, ABI, house-rule, and regression review.
Never count:
- code already exact at `6130661c`,
- code newly exact only because of a symbol alias without authenticated naming evidence,
- a donor’s claimed total,
- a function that another worker landed earlier in this lane,
- helper code not owned by the January object,
- or fuzzy code.
Keep a central assignment ledger so two workers never attack the same TU or function.
Historical leads such as `_bitmap_2d_sharpen`, `_bitmap_group_add_bitmap`, `_EncodeBlockRGBColorKey`, `_first_person_weapon_update`, `_hud_render_unit_interface`, `_hud_update_weapon`, motion-sensor updates, environment/lightmap/fog functions, and render-camera frustum functions must be remeasured. Their old labels are hypotheses, not instructions. Do not repeat a recorded negative shape unless new evidence directly refutes the old premise.
## 9. Parallel campaign architecture
Use agents aggressively, but safely:
- One implementation worker per `.c` file.
- Files must be strictly disjoint.
- No worker edits headers, config, shared docs, or another worker’s file.
- Workers use `tools/campaign/gate.py`, which writes PID-isolated scratch objects.
- Workers do not run `ninja`, `configure.py`, or any command that writes the shared `build/` tree.
- Workers do not commit.
- Workers record the exact current unit census before editing.
- Workers retain only source-authentic improvements and fully revert reductions in strict exactness.
- Each landed file receives an independent adversarial review by an agent other than its author.
- Only the orchestrator edits genuine shared headers/config, runs full builds, updates parks, and commits.
Use waves of disjoint TUs. Start with:
1. Unwritten functions.
2. Small functions capable of closing near-complete objects.
3. Structural mismatches with a named source-level defect.
4. Larger functions where call graph, frame, type width, relocation, or helper-boundary evidence gives a concrete route.
5. Pure allocation/x87/scheduling ties only when a new evidenced compiler law supplies a legitimate source lever.
Eight small closures are preferable to one speculative rewrite. However, do not abandon large objects forever: give their biggest functions dedicated research packets after the easy structural pool is harvested.
If a wave yields little, preserve its durable negative evidence and immediately pivot to a different mechanism. Do not spend multiple waves permuting declarations or random spellings.
## 10. Existing tools — use universal gates
Prefer the committed campaign tools over writing a bespoke Python test for every function:
```text
python tools/campaign/board.py
python tools/campaign/board.py <unit>
python tools/campaign/gate.py <unit> --fn <function>
python tools/campaign/gate.py <unit> --all
python tools/campaign/alndiff.py <unit> <function>
python tools/campaign/relocdiff.py <unit> <function> --count-by-target
python tools/campaign/tinfo.py <unit> --fn <function>
python tools/campaign/tinfo.py <unit> --data
python tools/campaign/branch_sweep.py <unit>
python tools/bijection_walk.py --unit <unit> <function> -v
python tools/fake_match_scan.py <changed paths>
python -m tools.campaign.unparked <unit>
python -m tools.campaign.stable_verdicts snapshot <file>
python -m tools.campaign.stable_verdicts diff <before> <after>
python -m tools.parked_functions
python -m tools.audit_semantic_matches
python tools/audit_object_admission.py
```
Use `alndiff.py`, not `gate.py --disas`, when instruction counts diverge; the latter is an index-aligned zip and hides insert/delete boundaries.
Use the existing frame-slot, x87-mod8, inline-boundary, cross-jump, scheduling-selection, and unclassified-triage detectors described in the compiler-law ledgers. Run their required ordering before inventing source shapes.
Spelling sweeps and compiler probes are diagnostic only. A winning spelling is not source-authenticity evidence by itself.
Do not add general tooling unless it solves a genuinely recurring gap. Any committed tool must be tested and documented; otherwise keep research scripts under untracked scratch evidence.
## 11. Headers and configuration
Header changes are high-risk because VC7 code generation is definition-position sensitive.
To keep the four concurrent lanes mechanically reconcilable, do not commit
changes to broad cross-lane headers such as `cseries.h`, `real_math.h`, core
object/tag headers, or a header genuinely owned by another lane. Preserve any
such requirement as a minimal proposed patch with consumer census and strict
before/after evidence, then continue elsewhere. Lane C may commit only genuine
bitmap, interface, rasterizer, or render owner-header changes whose complete
consumer set has been audited under the protocol below.
Before changing any header:
1. Identify every `.c` includer with `rg`.
2. Prove the declaration belongs in that header.
3. Determine whether a TU-private declaration is more authentic.
4. Compile pristine and patched header trees for every dependent TU.
5. Compare every dependent function’s size, relocations, normalized hash, emitted owners, and data ownership.
6. Run a full stable whole-tree diff.
7. Obtain an independent reviewer’s approval.
8. Commit the header prerequisite in an independently buildable packet.
Do not keep a header change that gains your object by regressing an unrelated object. Do not “repair” a regression with coincidental declaration ordering.
Configuration policy:
- `config/symbols.json`: edit only with authenticated January/PDB/source/caller evidence. Keep a symbol rename and the corresponding source identifiers in the same coherent commit.
- `config/parked.json`: orchestrator only. Remove a park when the function is newly exact. Rebaseline a credible fuzzy function only with fresh measurements and dated evidence. Never rebaseline a park to conceal a regression.
- `config/config.json` or object `Matching` status: change only after whole-object admission proves code, data, BSS, linkage, extra-owner, and source credibility.
- Never add a semantic-match override.
- Never broadly change compiler flags. `/QIfist` requires per-TU proof and full consumer/caller verification.
If a required owner header is shared with an out-of-scope unit, you may make the minimal genuine header change but may not edit the out-of-scope `.c` file. Every consumer must remain exact or unchanged.
## 12. COMDAT, owner, and inline safeguards
For every changed TU:
```text
python tools/campaign/gate.py <unit> --forbid-emitted-symbol _point_from_line3d --all
```
This must explicitly pass. Undefined references do not fail the guard; a candidate code definition does.
Also inspect:
- target and candidate code-symbol owner sets,
- COMDAT selection and storage class,
- helper bodies emitted only by the candidate,
- missing target helper owners,
- data/rdata/BSS/COMMON owners,
- relocation target identity and addends,
- private versus external linkage.
Do not gain a caller by changing the wrong helper owner. Preserve January’s observed inline schedule. Do not make shared `real_math.h` inline bodies `static` or otherwise globally change helper emission to fix one object.
Existing admitted asm such as `fast_ftol` or the already-reviewed color helper is function-specific evidence. It is not permission to hand-write arbitrary renderer bodies in assembly.
## 13. Baseline, checkpoint, and final verification
Before editing:
1. Run the full build.
2. Save a stable verdict snapshot.
3. Save `build/semantic_report.json`.
4. Record board totals and all in-scope per-unit gates.
5. Validate parks and object admission.
6. Record the baseline owner/data census for every assigned object.
At each coherent admission batch:
1. Review the source diff for house-rule compliance.
2. Run each changed unit’s full `gate.py --all`.
3. Run the `_point_from_line3d` emitted-symbol guard.
4. Review every target/candidate owner and relocation change.
5. Run the fake-match scanner over all changed source.
6. Run `ninja`.
7. Take a fresh stable snapshot and diff it from the frozen baseline and the previous checkpoint.
8. Require zero inherited exact regressions.
9. Validate parks.
10. Run semantic-match and whole-object admission audits.
11. Run the repository’s full pytest suite.
12. Run `git diff --check`.
13. Commit only when the tree is fully verified.
At final tip, require:
```text
ninja
python -m tools.campaign.stable_verdicts snapshot scratch/lane-c-after.json
python -m tools.campaign.stable_verdicts diff scratch/lane-c-before.json scratch/lane-c-after.json
python -m tools.parked_functions
python -m tools.audit_semantic_matches
python tools/audit_object_admission.py
python tools/fake_match_scan.py <all changed source/header paths>
python -m pytest -q
git diff --check
git status --short
```
If scratch content interferes with pytest discovery, move evidence outside the worktree rather than excluding real tests.
The final tracked tree must be clean. Keep bulky scratch objects and experimental variants outside Git.
## 14. Fuzzy preservation
If a credible reconstruction is the best current implementation but does not become strict exact:
- Keep it only if semantics, ABI, ownership, and house rules are sound.
- Measure size, normalized divergence, relocations, and first differing regions.
- Compare it with the baseline and every known donor.
- Document why it is better.
- Record remaining blockers and exact reopen criteria.
- Update its park evidence honestly.
- Give it zero strict credit.
- Do not sacrifice an exact sibling or whole-object owner to retain it.
A fuzzy implementation is useful future research, but it must never inflate the strict total.
## 15. Campaign behavior
Work autonomously. Do not pause for ordinary implementation questions. If one function needs an owner ruling, park that question, continue unrelated work, and present rulings in a compact batch.
Do not terminate because:
- the first wave was low yield,
- a previous ledger called something difficult,
- a function is parked,
- a header is shared,
- or the PDB lacks locals.
Instead, determine whether new evidence can satisfy the reopen condition. Conversely, do not repeatedly attack a proven allocator tie without a new source-level lever.
Every one of the 83,898 starting residual bytes must finish in one of these categories:
1. Strict exact and landed.
2. Credible fuzzy improvement landed at zero credit.
3. Evidence-backed structural blocker with a concrete reopen criterion.
4. Demonstrated compiler allocation/scheduling tie with the validating detector.
5. Owner/policy ruling required, with a minimal ready candidate.
6. Previously solved/duplicate at the frozen baseline and therefore excluded from new credit.
“Unexamined” is not an acceptable final category.
## 16. Commit and handoff requirements
Make small, independently buildable commits organized by coherent TU or owner packet. Avoid a tangled final megacommit. Header/config prerequisites and their affected source must remain in a clear, ordered series.
Do not push.
Create:
```text
docs/object_matching_logs/claude_lane_c_graphics_ui_HANDOFF_20260920.md
docs/object_matching_logs/claude_lane_c_graphics_ui_admission_manifest_20260920.json
```
The handoff must include:
- frozen base and final commit IDs,
- exact reverse-order cherry-pick range,
- every commit and changed file,
- baseline and final strict functions,
- baseline and final meaningful exact bytes,
- new meaningful bytes,
- new padded bytes,
- fuzzy bytes at zero credit,
- exact regressions, which must be zero,
- objects moved to function-only 100%,
- objects admitted as whole `Matching`,
- data/BSS/linkage/COMDAT owner changes,
- header/config changes and their full blast-radius evidence,
- parks removed or remeasured,
- tests and audits with exact counts,
- fake-scan findings and disposition,
- per-function sizes, hashes, relocation counts, and source evidence,
- rejected experiments and do-not-repeat findings,
- unresolved candidates and precise reopen criteria,
- confirmation that canonical was untouched and nothing was pushed.
The JSON manifest must be machine-readable and contain per-function before/after evidence, not merely headline totals.
Finish with a clean local branch and report:
```text
branch
base
tip
commit count
strict meaningful gain
strict padded gain
strict function gain
whole-object Matching gain
fuzzy bytes retained at zero credit
regressions
test/audit results
git status
nothing pushed
canonical untouched
```
@@ -1,77 +0,0 @@
# Claude Lane D — refreshed after A/B/C reconciliation
Work on Halo January 2342 **Halo-owned** code, not Xbox/vendor libraries. Start
an isolated local worktree and branch at the **latest fetched tip** of
`jonas/exact-pilots` (`https://github.com/bnunu/halo`). Verify that this tip
contains the audited A/B/C reconciliation commit
`4f4c85021db48dae61252d8e1301c60e1c2cc138`, record its full hash in
your handoff, and confirm a clean tree before editing. Do not start from the
stale `6130661c` base in the old Lane D brief. Never modify canonical or
push your lane.
Read these complete before selecting work:
- `docs/campaign_prompts/CLAUDE_REMAINING_LANE_D_SYSTEMS_EFFECTS_20260920.md`
for the full 49-TU Lane D allowlist, reference paths, workflow and house rules;
**this refresh overrides its baseline, numeric portfolio, cross-lane header
reservations, and absolute COMDAT rule**.
- `docs/campaign_house_rules.md`, `docs/matching_methodology.md`,
`docs/common_constants.md`, `docs/assertions.md`,
`docs/campaign_throughput_lessons.md`.
- `docs/object_matching_logs/claude_lane_a_ai_core_HANDOFF_20260920.md`,
`docs/object_matching_logs/claude_lane_a_HANDOFF_TO_LANE_B_20260921.md`,
`docs/object_matching_logs/claude_lane_b_HANDOFF_20260920.md`,
`docs/object_matching_logs/claude_lane_b_rejected_hypotheses_20260920.md`,
`docs/object_matching_logs/claude_lane_c_canonical_reconciliation_20260922.md`,
and `docs/common_pool_ordering_20260922.md`.
Re-measure the allowlist against **this** baseline. A/B/C already changed some
Lane D files, so no old residual size, park count, donor gain, or archived lane
report is current authority. Freeze a stable-verdict snapshot, per-object
function/data/owner census, parks, admission audit, build and tests before any
edit. Prioritize still-unwritten and structural gaps in projectiles/weapons,
decals/effects, HS compile/runtime, sound, geometry, and the smaller systems
objects; do not redo already exact rows. Aim for 40K new meaningful exact code
bytes while evidence supports it, but report the actual verified delta only.
Lessons that bind this lane:
1. Exact function credit requires a strict stable row, full build, unchanged
inherited exact rows, sound semantics, and an ownership audit. A fuzzy body
is useful source but earns zero exact bytes. Data scoring changed with
objdiff v3.6.0; separate scorer/accounting gains from newly reconstructed
source and keep `linker_common` at zero credit while incomplete.
2. Header definition position can alter unrelated TUs. Put each genuine owner
header change in a prerequisite commit, enumerate all consumers, and run a
board-wide per-row sweep. Do not hide a needed declaration in a consumer
`.c`. The A/B/C work has landed, so prior cross-lane reservations are lifted;
the blast-radius rule is not.
3. COMMON pool adjacency is only a negative constraint, **never** proof of a
global's owning TU. Do not add unreferenced or address-named globals to
complete `.data`/`.bss`. Require an independent original owner/type oracle.
4. A real shared-header inline COMDAT may be emitted under the documented
A/B/C narrow exception only when January's folded copy is byte-identical,
the caller is strict exact, the full owner census is reviewed, and no exact
sibling regresses. Do not hand-expand helpers to evade ownership. In
particular, preserve the January `_point_from_line3d` schedule.
5. Previously held UB, artificial duplicate work, pragmas, two-pass asm, and
source-shape steering do **not** become acceptable merely because they
byte-match. Reopen only on new primary evidence and an explicit owner
ruling. The rejected Lane C COMMON, null-write, HUD asm/pragma, and
lens-flare-overrun packets are examples, not backlog to re-land.
6. Use first-party January bytes/relocations, January MAP/PDB, the supplied
Bungie C files, HCEA/2020, Ghidra/DIA and Marathon with their correct
evidentiary limits. The later builds are not interchangeable with January.
Do not spend a wave on known pure register/x87 ties without a falsifiable
source, type or header hypothesis.
Use one worker per disjoint `.c`; workers use PID-isolated `gate.py` scratch
objects, never shared build/config. Orchestrator alone edits headers/config,
runs Ninja, performs the stable diff, commits, and updates parks. In each
5K–15K wave reject exact losses, invalid parks, invented owners, surplus
COMDATs, fake-scan leads, and nonsensical logic before banking. Keep a
machine-readable per-function manifest and an evidence-backed list of rejected
trials. End with local commits, a clean worktree, final tests/build/sweep and
handoff; **do not push**. Report strict meaningful code, padded code, fuzzy
zero-credit extent, data-only/scorer gains and whole-object completions
separately.
@@ -1,311 +0,0 @@
# Claude prompt — Lane D: effects, items, world data, sound, scripting, and systems
You are running one of four strictly disjoint Halo 1 January 2342 exact-match
lanes. Work aggressively and persistently, but never purchase byte equality
with fake source, undefined behavior, invented ownership, target rewriting, or
nonsensical logic.
## Fixed baseline and isolation
- Repository: https://github.com/bnunu/halo
- Baseline branch: jonas/exact-pilots
- Exact baseline commit: `6130661c9585fdb2785b3ff6bdb2ad0cfef0520c`
(Reconcile final Astra campaign, +4,073 bytes)
- Create an isolated worktree and branch from that exact commit, for example
C:\\halo-worktrees\\claude-remaining-lane-d-20260920 on
claude/remaining-lane-d-20260920.
- Confirm `git rev-parse HEAD` equals the full baseline hash and the tree is
clean.
- Never edit the canonical checkout. Never push. Do not merge or rebase another
campaign. Finish with local commits and a clean worktree.
- Read the whole repository and evidence corpus, but modify only Lane D files,
genuine Lane D owner headers after the header protocol below, uniquely named
Lane D documentation, and a final isolated bookkeeping commit.
The reconciled baseline has 1,443,979 / 1,770,166 accepted Halo meaningful
bytes (81.573084%), 7,291 / 7,574 accepted Halo functions, and 184 valid active
parks. All four lanes together cover 326,187 remaining Halo-owned bytes.
Lane D owns 87,255 meaningful residual bytes across 49 translation units:
32,593 written-unparked, 54,651 parked, and 11 unwritten. Target at least
40,000 new strict meaningful bytes, with a stretch goal of closing the full
portfolio. If 40K is not reached, continue until every unit has a verified
landing or a precise evidence-backed blocker. Do not declare the lane
unreachable from aggregate park counts.
## Exclusive Lane D allowlist
Only these translation units are in scope:
~~~text
source/bink/bink_playback 979
source/bungie_net/common/prime_numbers 340
source/bungie_net/network/transport_endpoint_set_winsock 1344
source/bungie_net/network/transport_endpoint_winsock 294
source/cache/cache_files_decompress_windows 2117
source/cache/cache_files_windows 746
source/cache/xbox_sound_cache 368
source/cache/xbox_texture_cache 209
source/camera/dead_camera 1512
source/camera/editor_flying_camera 2563
source/camera/observer 1561
source/cseries/profile 276
source/cseries/stack_walk_windows 1167
source/cutscene/cinematics 1272
source/effects/contrails 945
source/effects/decals 7930
source/effects/effects 3003
source/effects/particle_systems 2103
source/effects/player_effects 2355
source/effects/weather_particle_systems 2346
source/hs/hs 81
source/hs/hs_compile 2632
source/hs/hs_runtime 3404
source/items/items 2352
source/items/projectiles 7905
source/items/weapons 2224
source/main/main 2542
source/math/geometry 4775
source/math/periodic_functions 244
source/networking/network_connection 588
source/objects/damage 2663
source/objects/widgets/flags 1170
source/objects/widgets/glow 1729
source/objects/widgets/light_volumes 905
source/objects/widgets/lightning 2571
source/saved games/player_profile 1148
source/saved games/saved_game_files 359
source/sound/game_sound 1570
source/sound/ima_adpcm 404
source/sound/sound_dsound_xbox 630
source/sound/sound_manager 3966
source/structures/leaf_map 1950
source/structures/structure_detail_objects 66
source/structures/structure_lens_flares 4331
source/structures/structure_visibility 468
source/structures/structures 1210
source/tag_files/files_windows 209
source/text/draw_string 920
source/tool/connected_geometry 809
~~~
Numbers are current residual bytes, not promised gains. Do not work in libs/**,
xbox/**, vendor Bink objects, libcmt, d3d8, d3dx8, dsound, xapi, xnet, xkbd, or
any translation unit absent from the allowlist.
## Read before editing
Read these files completely:
~~~text
README.md
docs/campaign_house_rules.md
docs/matching_methodology.md
docs/common_constants.md
docs/assertions.md
docs/campaign_throughput_lessons.md
tools/campaign/README.md
docs/object_matching_logs/astra_90pct_HANDOFF_20260920.md
docs/object_matching_logs/astra_90pct_research_atlas_20260920.md
docs/object_matching_logs/astra_90pct_rejected_hypotheses_20260920.md
docs/object_matching_logs/astra_90pct_admission_manifest_20260920.json
docs/object_matching_logs/astra_90pct_research5_shared_context_20260920.md
docs/object_matching_logs/astra_90pct_research5_shared_schedule_20260920.md
docs/object_matching_logs/astra_90pct_research5_projectiles_20260920.md
docs/object_matching_logs/astra_90pct_research5_structure_lens_20260920.md
docs/object_matching_logs/astra_90pct_research5_sound_type_rejected_20260920.md
docs/object_matching_logs/astra_90pct_research8_damage_20260920.md
docs/object_matching_logs/astra_90pct_research8_damage_independent_20260920.md
docs/object_matching_logs/astra_90pct_research8_sound_listener_20260920.md
docs/object_matching_logs/astra_90pct_research10_weapon_projectiles_20260920.md
docs/object_matching_logs/astra_90pct_research10_weapon_projectiles_independent_20260920.md
docs/object_matching_logs/astra_90pct_research12_weapons_tail_20260920.md
docs/object_matching_logs/astra_90pct_research12_weapons_owner_independent_20260920.md
docs/object_matching_logs/astra_90pct_wave4_decals_20260920.md
docs/object_matching_logs/astra_90pct_wave4_decals_independent_review_20260920.md
~~~
Search current per-object logs before reopening any park. Useful read-only
primary and cross-build material:
~~~text
C:\\Users\\isabe\\Downloads\\Halo Combat Evolved (Jan 14, 2002 prototype)
C:\\Users\\isabe\\Downloads\\Halo - Combat Evolved (Oct 12, 2001 prototype)\\Halo 2276
C:\\Users\\isabe\\Downloads\\haloleak2024\\halo1\\network_client_manager.c
C:\\Users\\isabe\\Downloads\\haloleak2024\\halo1\\network_server_manager.c
C:\\Users\\isabe\\Downloads\\haloleak2024\\halo1\\random_math.c
C:\\Users\\isabe\\Downloads\\haloleak2024\\halo1\\h1_tags
C:\\Users\\isabe\\Documents\\Codex\\2026-07-13\\i-w\\_research-marathon-source-agent-20260904
C:\\Users\\isabe\\Documents\\Codex\\2026-09-20\\files-pasted-by-the-user-halo\\outputs\\astra_90pct_function_atlas.csv
~~~
January's PDB has publics but no useful Halo locals or line records. The
2020/debug executables have confirmed source differences. Use them as
typed/control-flow evidence, not interchangeable source. IDA Free has no
callable automation endpoint. Use Ghidra headless, DIA/PDB tools, January raw
bytes and relocations, original MAP names, the supplied Bungie source, and
authenticated cross-build windows.
## Mandatory house rules
1. Put void on its own line in every no-argument parameter list.
2. Put every parameter on its own line.
3. End every function with an explicit valid return.
4. Use subsystem typed macros wrapping tag_get.
5. Use typed object macros instead of raw object_get plus casts.
6. Preserve January's inline schedule; emit no new point_from_line3d COMDAT.
7. Give private functions authentic/descriptive names, never code plus address.
8. Give globals authentic/descriptive names, never bss plus address.
9. Put prototypes in genuine owners, not unrelated consumers.
10. Use inline assembly only for evidenced low-level/helper/math instructions
with no faithful VC7 C spelling.
11. Retain and document credible fuzzy code at zero credit when easy exactness
fails; park it for later.
12. Prefer small closable functions before one giant residual.
13. Do not force-inline or duplicate helper bodies to steer code generation.
14. Marathon is style/semantic reference, not January byte proof.
15. With no dedicated header, use the closest genuine associated header.
16. Use named correct enum constants in switches.
17. Declare and initialize variables together when practical.
18. Prefer cseries.h macros over manual bit operations.
19. Use project types such as real, boolean, byte, short, and long.
20. Reject fake matching and implausible original source.
21. Reject nonsensical logic even when bytes match.
22. Use /QIfist only when target/compiler evidence requires it per TU and all
affected callers/conversions have been audited.
Use the repository's verified assert/dassert/vassert, warn/dwarn/vwarn, and
halt/dhalt/vhalt contracts from docs/assertions.md. Do not manually expand them.
Preserve a target-proven original bug only when primary evidence is decisive,
the semantics are documented with a BUG comment, and independent review agrees.
Never introduce undefined behavior merely to shape bytes.
## Workflow
1. Establish the full baseline:
~~~text
ninja -j4 all_source progress semantic_progress
python -m pytest -q tools
python -m tools.campaign.stable_verdicts snapshot scratch/lane-d-before.json
python -m tools.parked_functions
python tools/audit_object_admission.py --output scratch/lane-d-admission-before.json
~~~
2. Build a Lane D ledger. For every row record meaningful and padded target
size, candidate size, relocation count, normalized hash, objdiff percent,
park class, reopen criterion, and prior experiments. Re-measure locally; the
Astra CSV is routing evidence, not authority.
3. Attack in this order:
- genuinely unwritten functions and missing coherent source graphs;
- written-unparked structural differences involving calls, branches, fields,
locals, types, or ownership;
- small near-complete functions with new primary evidence;
- parks only when evidence satisfies or refutes their reopen criteria.
Do not spend a wave on pure register allocation, independent-load ordering,
or x87 stack ties without a falsifiable source/type/header hypothesis.
4. Highest leverage: projectiles and weapons; decals, structure lens flares,
effects, particles and weather; HS compile/runtime; sound manager/game sound;
geometry/math with natural helpers; then small cache, camera, object-widget,
save, text and tool units. Use the three Bungie C files as style and semantic
maps where applicable, not January byte authority.
5. Use PID-isolated scratch objects:
~~~text
python tools/campaign/gate.py <unit> --fn <function> --out scratch/<unique>.obj
python tools/campaign/alndiff.py <unit> <function> --ours-object scratch/<unique>.obj
python tools/campaign/relocdiff.py <unit> <function> scratch/<unique>.obj --count-by-target
python tools/campaign/tinfo.py --object scratch/<unique>.obj --data
~~~
Use gate --edits or scratch source copies for bounded experiments. A gate hit is
necessary but not sufficient: independently review semantics, source
plausibility, relocations, owners, non-code sections, and siblings.
6. If using subagents, assign exactly one C file per worker. Workers use only
gate.py and unique scratch objects; they do not run Ninja, edit headers or
config, commit, or push. The orchestrator reproduces every hit.
7. Header protocol: edit only a genuine owner; enumerate every C consumer with
rg first; freeze exact controls and owner/data/COMMON/COMDAT state; compile
every consumer; put a header prerequisite in its own commit; reject any
inherited exact loss or unsupported owner. If a required shared header is
owned by another lane, preserve a proposed patch and evidence but do not
edit it.
To minimize four-lane reconciliation, do not commit changes to broad
cross-lane headers such as cseries.h, real_math.h, or core object/tag
headers. Preserve those as separately reviewable proposed patches with a
complete consumer census and strict before/after evidence. Commit only
narrow genuine Lane D subsystem owner headers after the protocol above.
8. Shared config protocol: never change comparators, target objects, build
metadata, global optimizer defaults, or semantic-credit rules. symbols.json
requires original identity evidence. Matching requires full code, data, BSS,
relocation, COMDAT, linkage, surplus-owner and source-authenticity audits.
Keep source commits separate. Put minimal park changes in a final bookkeeping
commit and duplicate the exact delta in the manifest for conflict-free
integration.
9. Run the hard point_from_line3d emitted-symbol guard on every changed TU
unless January genuinely owns it there. For inherited genuine owners, compare
the complete before/after owner census. Reject surplus fast_ftol, pixel,
math, or XDK helper owners unless authentic topology and link selection are
proved. Equal bytes in another object are not ownership proof.
10. Preserve best credible fuzzy bodies only at zero credit. Do not count fuzzy
target extent, objdiff percentage, or padded envelope as exact progress.
## Wave gates and accounting
Use coherent 5K–15K candidate waves. Before and after each landing, run full
build and stable snapshots. At minimum:
~~~text
ninja -j4 all_source progress semantic_progress
python -m tools.campaign.stable_verdicts snapshot scratch/lane-d-after-N.json
python -m tools.campaign.stable_verdicts diff scratch/lane-d-before-N.json scratch/lane-d-after-N.json
python -m tools.parked_functions
python tools/audit_object_admission.py --output scratch/lane-d-admission-N.json
python tools/fake_match_scan.py --format json <changed files>
git diff --check
~~~
At each cumulative 10K meaningful checkpoint and final closeout also run the
full tools tests and global fake scan. Audit exact bytes, ordered relocation
identities, inherited matches, code/data/BSS/COMMON owners, COMDAT copies,
externals, names/linkage, and non-code sections.
Report separately: strict meaningful bytes, strict padded bytes, and fuzzy
target extent at zero credit. Never derive canonical gain from donor headlines.
## Commits and handoff
- Commit only independently verified packets, one coherent source/owner packet
per commit. Header prerequisites precede source; park/config bookkeeping is
last.
- Do not add scratch, build outputs, binaries, PDB/MAP/EXE files, generated
atlases, or large probe reports.
- Use uniquely named Lane D records. End clean and do not push.
Create:
~~~text
docs/object_matching_logs/claude_remaining_lane_d_HANDOFF_20260920.md
docs/object_matching_logs/claude_remaining_lane_d_admission_manifest_20260920.json
~~~
The handoff must state base/tip, ordered commits, changed files, per-function
meaningful/padded bytes and fingerprints, strict gains, zero-credit fuzzy work,
rejected trials, parks/config/header changes, owner/COMDAT/data results, exact
commands/results, and blockers with concrete reopen criteria. The manifest must
contain machine-readable measurements and exact proposed shared-config deltas.
Do not stop after a few easy leaves. Continue through the allowlist while
tokens remain, switching quickly from exhausted scheduling ties to a different
unit with structural evidence. Authenticity and zero regressions remain
absolute under the 40K goal.
-25
View File
@@ -1,25 +0,0 @@
# Four-lane remaining-Halo campaign (2026-09-20)
These four self-contained Claude prompts partition every Halo-owned meaningful
residual byte measured at canonical commit
`6130661c9585fdb2785b3ff6bdb2ad0cfef0520c`. They deliberately exclude Xbox
SDK/XDK and other vendor-library objects.
| Lane | Scope | Units | Residual bytes | Prompt |
| --- | --- | ---: | ---: | --- |
| A | AI core, actors, perception, communication, encounters | 11 | 79,657 | `CLAUDE_REMAINING_LANE_A_AI_CORE_20260920.md` |
| B | AI actions/pathfinding, gameplay, physics/collision, units/vehicles | 31 | 75,377 | `CLAUDE_REMAINING_LANE_B_SIMULATION_PHYSICS_20260920.md` |
| C | Bitmaps, UI/HUD, Halo-owned Xbox rasterizer, rendering | 30 | 83,898 | `CLAUDE_REMAINING_LANE_C_GRAPHICS_UI_20260920.md` |
| D | Effects, items, scripting, sound, structures, systems | 49 | 87,255 | `CLAUDE_REMAINING_LANE_D_SYSTEMS_EFFECTS_20260920.md` |
| **Total** | **All remaining Halo-owned code** | **121** | **326,187** | |
The translation-unit allowlists are mutually exclusive. Each prompt requires a
dedicated worktree at the exact baseline, forbids pushes and canonical edits,
requires current-state anti-duplication checks, applies all house rules, keeps
fuzzy work at zero credit, and demands a clean machine-readable handoff for
later reconciliation.
Run all four lanes in parallel only when each Claude session has its own
worktree. Give each session exactly one prompt; do not combine the prompts or
allow a lane to cross its allowlist. Canonical accounting remains authoritative
over worker headline totals.
-218
View File
@@ -1,218 +0,0 @@
# Campaign throughput lessons (2026-08-30 / 2026-08-31)
Operational lessons from a run that took the board from **669,879 to 714,807
matched bytes (+44,928)** and **4,829 to 4,973 credited functions (+144)** with
**zero regressions**, verified by a full build plus a per-function sweep of all
569 built units.
This document is about *throughput and hazards* - how to close many functions
in parallel without breaking what already works. For per-function matching
technique see `matching_methodology.md` and
`exact_match_acceleration_playbook.md`.
---
## 1. Write unwritten functions before fighting residuals
This is the single highest-leverage finding. Measured across four waves:
| wave | scope | approach | bytes gained | yield |
| --- | --- | --- | --- | --- |
| 1 | 71,112 B residual | close residuals | 11,040 | 16% |
| 3 | 27,920 B "sha-only" residual | close residuals | 3,168 | 11% |
| 4 | ~160,000 B unwritten | write missing functions | **30,720** | far larger scope |
Wave 3 deliberately picked the *closest* residuals on the board - functions
whose padded size **and** relocation count already matched, so only instruction
selection differed - and still returned only 11%. Those residuals are typically
a single register-allocation or x87 stack-discipline decision, and they resist
source-level control.
An **unwritten** function has no wrong prior to undo. You read the target
disassembly and write natural C89. That is a fundamentally easier problem.
The board currently holds roughly **1,000,000 bytes of unwritten functions**
against **100,000 bytes of residual**. Work the unwritten pool first.
Within a unit, start with the **small** functions. Eight 300-byte closures beat
one failed heroic attempt at a 3,000-byte function.
## 2. Parallel fan-out architecture
`tools/campaign/gate.py <unit>` compiles **one** translation unit to a
PID-named object under `scratch/` and never writes to `build/`. That makes it
safe to run from many workers simultaneously.
The architecture that worked:
- **One worker per `.c` file, files strictly disjoint.** Never two workers in
one file.
- Every worker measures **only** with `gate.py`. Useful flags: `--fn <FUNC>`,
`--disas <FUNC>`, `--source <FILE>` (compile a variant without touching the
real file), `--edits <json>`.
- **No worker runs `ninja` or `configure.py`.** A shared build directory cannot
take concurrent writers.
- **No worker commits.** The orchestrator does one full build, one board
measurement, and one commit at the end.
- Workers record `gate.py`'s exact count *before* starting and revert anything
that lowers it.
### `--disas` hides insert/delete holes
`gate.py --disas` is an **index-aligned zip**, not an alignment diff. Once
instruction counts diverge, every later row reads as different and a genuinely
inserted or deleted block is invisible. Use `scratch/alndiff.py` (difflib
SequenceMatcher over normalized capstone streams, branch targets normalized):
python scratch/build1.py <unit>.c scratch/cur.obj
python scratch/alndiff.py <unit> <FUNC> scratch/cur.obj
A 48-instruction hole in `_ai_debug_render_actor` was only visible this way.
Ignore `.rdata` / `__real@...` annotation mismatches in that output - those are
symbol-naming artifacts, not code differences.
## 3. Header edits perturb objects that never name the added types
**"Edit only your own object's header" is not a safe rule.**
`source/items/weapons.h` is included by ten other `.c` files. Adding two
interface-state structs to it silently dropped `units.obj` from **189/189 to
188/189**, breaking `_unit_preprocess_node_orientations` - a function that
names none of those types and calls none of the new functions.
Bisecting the three added blocks:
| added to weapons.h | units.obj |
| --- | --- |
| enum only | 189 ok |
| structs only | **188 BREAKS** |
| protos only | 189 ok |
| enum + structs | 189 ok |
| structs + protos | **188 BREAKS** |
| all three | **188 BREAKS** |
Adding the enum *in front of* the structs repairs it. The effect is not
semantic - it is the C2 allocator's **definition-position** sensitivity. What
perturbs codegen is where declarations sit, not what they mean.
### Rules that follow
1. Before touching any `.h`, run
`grep -rl "<header filename>" source/ --include=*.c`. If anything else
includes it, default to putting the declaration in the `.c`.
2. Definitions private to one TU belong in that TU. The weapons fix: structs
moved into `weapons.c`; the header kept the enum, the prototypes, and a
forward declaration so the prototype's struct tag has file scope. `units`
returned to 189/189 with all 50 of weapons' exact functions intact.
3. **Never** "fix" this by relying on a coincidental ordering that happens to
restore the victim.
4. If a header edit is genuinely required, prove it: build **shadow header
trees** (patched and pristine), compile every dependent unit against both,
and compare per-function size, relocation count and normalized hash. This
was done to drop `__declspec(align(4))` from `struct guard_state_data` in
`actions.h` (24 includers) and it held up under independent re-check.
### Some cases have no local remedy
Adding two trailing fields to `struct _projectile_datum` in `projectiles.h` -
fields January demonstrably uses at composite offsets 0x1FC and 0x200 - drops
`units.obj` to 188/189. The type is public, so internalising it in the `.c`
does not apply. About 1,152 bytes of projectile work is gated on this. That is
an owner decision; do not trade away a complete object for it quietly.
## 4. Verification discipline
**Worker self-reports are systematically optimistic.** One wave's rolled-up
"confirmed bytes" read 18,544 against 3,168 actually closed, because verifiers
counted pre-existing exact functions alongside new ones.
The only numbers that count:
1. A full `ninja` build. Its semantic gate fails closed if any previously exact
function stops matching.
2. `python tools/campaign/board.py` for the byte and function totals.
3. A **per-function regression sweep**: snapshot the exact set of every unit
from `build/split` vs `build/base` before the run, and re-compare after.
Object-level counts hide one object lost against another gained.
`board.py`'s per-object listing is **truncated** - an object appearing in or
disappearing from it is not evidence of a change. Use the sweep.
## 5. Cross-cutting codegen facts confirmed this run
- **Frame size is structural.** A differing `sub esp, N` means a local's scope
or type is wrong. It is never scheduling noise. This is what reopened
`_generate_prime_numbers` after a prior session had declared evidence
exhaustion on it (81.61% -> 91.15%).
- **Local declaration order is inert** for stack-slot assignment - proven
byte-identical across four permutations. Do not spend waves permuting
declarations.
- **The zero-register pin.** January frequently keeps a zero live in a register
and spends it repeatedly (`mov [mem],reg`, `push reg`, `cmp reg,zeroreg`)
where our build rematerialises immediates. Pinning a zero consumes a
callee-saved register, so this surfaces *downstream* as stack-slot
permutations and duplicated or merged blocks rather than as a local byte
difference. It is not a compiler flag: on `_lra_allocate`, `/O1`, `/O2 /Os`
and `/O1 /Ob2` all shrink the function to 319 bytes and `/Ox /Os` explodes it
to 1255, while the campaign flags give the closest result at 368.
- **Shared-header inlines must stay non-`static`.** Converting the 89
`__inline` definitions in `real_math.h` (plus 6 elsewhere) to
`static __inline` costs **21 objects and 119 functions**: `static` lets VC7
dead-code the out-of-line copy and inline more aggressively, changing call
sites. This also kills the theory that our ~20-28 surplus COMDATs per object
come from storage class - non-static is what reproduces January's bytes, so
the surplus-COMDAT question remains open and is a whole-tree property, not
something an individual object's reconstruction introduced.
## 6. Guards against fake matching
A byte match bought with source Bungie would not have written is worth less
than an honest residual. Rejected on sight: `volatile`, `register`, `#pragma`,
barriers, compiler-flag changes, raw numeric struct offsets, type punning or
representation casts, undefined behaviour, byte forcing, and logic that is
nonsensical for the function's stated job and only coincidentally matches.
Three live examples from this run, all correctly refused:
- The only shape that narrowed `_ai_debug_render_actor` required
`real_vector3d *` to `real_point3d *` casts to reach `point_from_line3d`.
That is representation punning, so it was reverted - and re-reading the
numbers, it only reached 24,864 of 24,976 bytes anyway. It was never a
closure, just a 16-byte improvement on a 128-byte gap.
- Writing an early return as `*num_primes = i` to deliberately spend a live
zero is a coincidence generator, not a reconstruction. XDK 3911 folds it to
identical code and it was already on a do-not-repeat list.
- A `vehicle_update` shape that forced an increment into both arms of a clamp
using two invented file statics measured the right size only because the
shortfall was cancelled downstream by alignment padding. The real shape is
the `cseries.h` `MIN` macro with the constant on the left and no local
increment at all.
Inline assembly is allowed **sparingly** and **unit-locally** in small helper
and math functions. The admitted case is `fast_ftol` (`fld` / `fistp`), because
`/QIfist` lowers every C float-to-int conversion through a 64-bit `fistp qword`
and a 32-bit inline `fistp` is never compiler output. Keep it `static __inline`
in the `.c` that needs it - never in a shared header.
## 7. Reusable scratch tooling
Left in `scratch/` by this run (note that `scratch/` is per-worktree and is not
committed, so these exist only in the worktree that produced them):
| file | purpose |
| --- | --- |
| `alndiff.py` | true alignment diff over normalized capstone streams |
| `build1.py` | compile one unit with the real build flags to a chosen `.obj` |
| `cs_build.py`, `cs_gate.py`, `cs_blast2.py` | shadow-header-tree blast-radius harness |
| `pn_score.py` | score source variants by byte divergence and first-diff offset |
| `pn_try.py` | batch-apply source variants through `gate.py` and report |
Use the committed `tools/fake_match_scan.py` for fake-match review; unlike the
scratch helpers above, it is available in every checkout.
A caution learned twice the hard way: **Python heredocs in this environment eat
one level of backslashes.** Build Windows path separators with `chr(92)` rather
than writing them literally, or you will silently emit a single-backslash
`__FILE__` string where the source needs a doubled one and break relocation
identity against the target's string literal.
-355
View File
@@ -1,355 +0,0 @@
# Common constants and representation reference
Verified against canonical on 2026-09-08. Start here before guessing literals.
This is a lookup guide, not a new constants owner. Most rows record high-use
names that already have canonical owners; the supplied-source crosswalk is
explicitly separate. Future reconstructions should include the
owning header and use the existing symbol or macro; they should not duplicate
these declarations in a translation unit merely to obtain a desired code
shape.
`Confirmed` below means the value and spelling are present in the named
canonical owner. Float bits show the rounded binary32 representation; tag
immediates use the pinned MSVC convention. January corroboration is called out
explicitly below. A recognizable bit pattern is a lookup clue, not proof of
which source expression produced it. `Context-specific` means a value belongs
to the stated enum, storage type, or algorithm, not an unrelated global owner.
## Core values and representations (confirmed)
| Purpose | Canonical symbol / type | Value or representation | Owning source | Important boundary |
| --- | --- | --- | --- | --- |
| Boolean true/false | `TRUE`, `FALSE` | `1`, `0` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Use these for the project's one-byte `boolean` APIs. A `boolean` i386 return may define only `AL`; a `void` return promises no value. Do not require unrelated upper `EAX` bits. |
| Invalid signed index/handle | `NONE` | `-1` (`0xFFFFFFFF` as a 32-bit `long`) | [source/cseries/cseries.h](../source/cseries/cseries.h) | This is not `NULL`. Confirm the destination width: a byte or word sentinel has the narrowed all-ones representation, and some APIs use zero as a distinct valid or invalid value. |
| Simulation rate | `TICKS_PER_SECOND`, `ACTUAL_TICKS_PER_SECOND` | `30`, `30` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Express seconds-to-ticks as `seconds * TICKS_PER_SECOND`; express per-tick scale as `1.0f / TICKS_PER_SECOND`. Do not invent a `SECONDS_PER_TICK` owner. The latter folds to float bits `0x3D088889`. |
| Wall-clock unit | `MILLISECONDS_PER_SECOND` | `1000` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Do not mix millisecond tag fields with 30 Hz game ticks. Existing code explicitly converts when needed. |
| Display cadence | `VBLANKS_PER_SECOND` | `60` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Vblanks are not simulation ticks even though both are integer rates. |
| Integer widths | `LONG_BITS`, `SHORT_BITS`, `CHAR_BITS` | `32`, `16`, `8` | [source/cseries/cseries.h](../source/cseries/cseries.h) | These describe the January/Win32 ABI. Preserve signedness as well as width. |
| Integer extrema | `LONG_MAX`, `LONG_MIN`, `UNSIGNED_LONG_MAX`, `SHORT_MAX`, `SHORT_MIN`, `UNSIGNED_SHORT_MAX` | `2147483647`, `-2147483648`, `0xFFFFFFFF`, `32767`, `-32768`, `0xFFFF` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Prefer the named bound. `UNSIGNED_LONG_MAX` is also the canonical all-object mask, but that does not make it a universal sentinel. |
| Scalar real | `real` | `typedef float real` (4-byte IEEE-754 binary32 in target ABI) | [source/cseries/cseries.h](../source/cseries/cseries.h) | Use `f` suffixes for ordinary `real` constants. Use `double` only where the genuine API or default vararg promotion requires it. |
| Engine boolean | `boolean` | `typedef byte boolean`; `byte` is `unsigned char` | [source/cseries/cseries.h](../source/cseries/cseries.h) | It is one byte, not C/C++ `bool` and not a four-byte `long`. This affects fields, parameters, stack snapshots, and return checks. |
| Tag word | `tag` | `typedef unsigned long tag` (32 bits in target ABI) | [source/cseries/cseries.h](../source/cseries/cseries.h) | Preserve the four-character group token and the unsigned 32-bit type. |
| Coordinate axes | `_x`, `_y`, `_z` | `0`, `1`, `2` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Use these only as component/projection indices. They are not vector values or bit masks. |
| Geometric tolerance | `_real_epsilon` | `0.0001f`, binary32 bits `0x38D1B717` | [source/math/real_math.h](../source/math/real_math.h) | This is not a universal replacement for exact-zero tests. January functions use both epsilon and exact `0.0f`; preserve the observed predicate and NaN behavior. |
| Pi | `_pi` | `(real)M_PI`, binary32 bits `0x40490FDB` | [source/math/real_math.h](../source/math/real_math.h) | Use the project cast rather than a hand-rounded decimal when the source works in `real`. |
| Angle conversion | `DEGREES_TO_RADIANS`, `RADIANS_TO_DEGREES` | `((real)angle) * _pi / 180.f` or `((real)angle) * 180.f / _pi` | [source/math/real_math.h](../source/math/real_math.h) | Angles stored in tags are not uniformly degrees or radians. First authenticate the field's unit; then use the owner macro. Expression order can affect exact float bytes. |
| Finite real envelope | `REAL_MIN`, `REAL_MAX` | `-3.4028235e38f`, `3.4028235e38f` | [source/math/real_math.h](../source/math/real_math.h) | `REAL_MIN` here is the most-negative finite bound, not the C-library meaning “smallest positive normal.” |
January examples that corroborate the float rows are the
`__real@38d1b717`, `__real@41f00000`, and `__real@3d088889` relocations in
`build/split/source/objects/damage.obj`. They represent `_real_epsilon`,
`(real)TICKS_PER_SECOND`, and `1.0f / TICKS_PER_SECOND`; they do not justify
replacing the source-level owners with hexadecimal literals.
## Canonical macros whose semantics matter
| Macro | Definition owner | Correct use | Pitfall |
| --- | --- | --- | --- |
| `MIN(a,b)`, `MAX(a,b)` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Ordinary project min/max spelling when January establishes the same comparison and argument order. | Each expression can be evaluated more than once. Argument order controls signed-zero and NaN outcomes: do not normalize or swap operands merely because finite results look equivalent. |
| `FLOOR`, `CEILING`, `PIN` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Named lower/upper/clamp operations. | `PIN` is a macro composition, not an IEEE `fminf`/`fmaxf` substitute. It may evaluate the value repeatedly and lets a NaN value pass through under its current ordering. |
| `FLAG(bit)` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Convert an enum *bit index* to a mask. | Most `_..._bit` enum members are indices, not masks. Do not apply `FLAG` again to an existing `_..._mask_*` value. Preserve the owning integer width, especially near bit 31. |
| `TEST_FLAG`, `SET_FLAG` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Test or mutate a named bit in the authenticated field. | Do not replace an enum bit with a raw hex mask. Avoid side-effecting lvalues/expressions because these are macros. |
| `VALID_FLAGS(flags,bits)` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Validate that no bit at or above the declared count is set. | `bits` is a count, not a mask; signedness and shift width matter. |
| `BIT_VECTOR_SIZE_IN_LONGS`, `BIT_VECTOR_TEST_FLAG`, `BIT_VECTOR_SET_FLAG` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Long-word-packed bit vectors, 32 flags per `long`. | The macros index a `long` array. Do not silently cast an original `byte *` API to this representation. A byte-buffer use needs independent owner/caller evidence, as documented for the collision BSP boundary in [docs/object_matching_logs/collision_bsp_obj_sphere_cluster_20260904.md](object_matching_logs/collision_bsp_obj_sphere_cluster_20260904.md). |
| `NUMBEROF(array)` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Compile-time count of an actual array in the same scope. | On a pointer it returns a pointer-size ratio, not the pointee count. |
| `VALID_INDEX(index,count)` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Signed lower-bound and upper-bound check. | Preserve the signed type. Replacing it with an unsigned comparison can alter code shape and invalid-negative behavior. |
| `DATUM_INDEX_NEW`, `DATUM_INDEX_TO_ABSOLUTE_INDEX`, `DATUM_INDEX_TO_IDENTIFIER` | [source/cseries/cseries.h](../source/cseries/cseries.h) | Construct or split the 16-bit absolute index / 16-bit identifier handle representation. | A datum handle is not a plain array index. Mask before indexing; do not discard the identifier except at a proven boundary. |
| `TAG_BLOCK_GET_ELEMENT` | [source/tag_files/tag_groups.h](../source/tag_files/tag_groups.h) | Typed access through `tag_block_get_element_with_size(..., sizeof(type))`. | The type and element size are part of the runtime contract. Avoid a raw `address + index*stride` replacement. |
## Tag strings and common group IDs (confirmed)
`TAG_STRING_LENGTH` is **31** ([source/tag_files/tag_files.h](../source/tag_files/tag_files.h)). A
canonical fixed tag string buffer that includes its terminator is therefore
usually `char name[TAG_STRING_LENGTH + 1]`, or 32 bytes. The constant is a
payload limit, not permission to omit the final NUL.
MSVC packs the following four-character constants into the shown immediate
value. Always write the named owner; the hex form is provided only to read
disassembly and relocations. On little-endian x86 the bytes in memory appear
least-significant byte first.
| Domain | Symbol | Source token / i386 immediate | Owner |
| --- | --- | --- | --- |
| Game globals | `GAME_GLOBALS_TAG` | `'matg'` / `0x6D617467` | [source/game/game_globals.h](../source/game/game_globals.h) |
| Scenario | `SCENARIO_TAG` | `'scnr'` / `0x73636E72` | [source/scenario/scenario_definitions.h](../source/scenario/scenario_definitions.h) |
| Structure BSP | `STRUCTURE_BSP_TAG` | `'sbsp'` / `0x73627370` | [source/structures/structure_bsp_definitions.h](../source/structures/structure_bsp_definitions.h) |
| Base object definition | `OBJECT_DEFINITION_TAG` | `'obje'` / `0x6F626A65` | [source/objects/object_definitions.h](../source/objects/object_definitions.h) |
| Damage effect | `DAMAGE_EFFECT_DEFINITION_TAG` | `'jpt!'` / `0x6A707421` | [source/objects/damage_effect_definitions.h](../source/objects/damage_effect_definitions.h) |
| Bitmap | `BITMAP_GROUP_TAG` | `'bitm'` / `0x6269746D` | [source/bitmaps/bitmap_group.h](../source/bitmaps/bitmap_group.h) |
| Sound | `SOUND_DEFINITION_TAG` | `'snd!'` / `0x736E6421` | [source/sound/sound_definitions.h](../source/sound/sound_definitions.h) |
Four-character C constants are implementation-defined outside this compiler
context. Do not byte-swap them manually or replace a typed getter such as
`object_definition_get` with a raw `tag_get` call only to expose the numeric
immediate.
## Common engine enums and limits (confirmed, domain-specific)
| Domain | Symbol(s) | Value | Owner and boundary |
| --- | --- | --- | --- |
| Local rendering/input players | `MAXIMUM_LOCAL_PLAYERS` | `4` | [source/game/players.h](../source/game/players.h). This is not the network/player-datum capacity. |
| Network join token | `NETWORK_JOIN_GAME_TOKEN_SIZE` | `16` | [source/networking/network_messages.h](../source/networking/network_messages.h). Protocol byte count, corroborated by the supplied source and January's generator/callers; restored in the canonical protocol owner, not a claim about the absent original header. |
| Network header direction | `_byte_order_host`, `_byte_order_network` | `0`, `1` | [source/bungie_net/common/message_header.h](../source/bungie_net/common/message_header.h). January inbound reads pass 0; outbound connection/key/rejection writes pass 1. The former inferred host/network labels were reversed; the corrected names preserve every caller's immediate and emitted code. |
| Connection creation flags | `_connection_create_server_bit`, `_connection_create_clientside_client_bit`, `_connection_create_serverside_client_bit` | Bit indices `0`, `1`, `2`; masks `1`, `2`, `4` | [source/networking/network_connection.h](../source/networking/network_connection.h). Use `FLAG` at creation calls; these are not the separate closed/stale state bits. |
| Network game ports | `NETWORK_GAME_SERVER_PORT`, `NETWORK_GAME_CLIENT_PORT` | `0x141E`, `0x141F` / `5150`, `5151` | [source/networking/network_game_protocol.h](../source/networking/network_game_protocol.h). Shared only by actual protocol consumers; values are not transport-type ordinals. |
| Server rejection reasons | `_rejection_code_version_too_old` through `_rejection_code_blacklisted_machine`; `NUMBER_OF_SERVER_REJECTION_CODES` | Reasons `0..6`, count `7`; game full `4` | [source/networking/network_game_protocol.h](../source/networking/network_game_protocol.h). Serialized rejection field is a `word`; count is an exclusive bound, not a valid reason. |
| Connection timeout disabled | `_connection_dont_timeout` | `0` | [source/networking/network_connection.h](../source/networking/network_connection.h). The idle API tests a nonzero timeout before enforcing it. This literal is not the separate `global_connection_dont_timeout` debug boolean. Name from supplied source, value from January, header placement reconstructed. |
| Public-endpoint datagram capacity | `DATAGRAM_MAXIMUM_SIZE` | `400` / `0x190` bytes | [source/networking/network_connection.h](../source/networking/network_connection.h). Shared connection/provider limit, distinct from the reliable-message receive capacity below. `network_connection_read` takes a `word *` size; a DWORD stack-slot initialization does not make this a 32-bit API. |
| IPv4 loopback address | `IPV4_LOOPBACK_ADDRESS` | `0x7F000001` | [source/bungie_net/network/transport_address_constants.h](../source/bungie_net/network/transport_address_constants.h). Source-attested spelling and January compare immediate. Use in the transport address's host-order IPv4 field, not as four already serialized network bytes. This narrow shared header is included by the actual client/server consumers. |
| Client/server receive buffer | `MAXIMUM_NETWORK_MESSAGE_SIZE` | `0x800` / `2048` bytes | Local manager enums in [server](../source/networking/network_server_manager.c) and [client](../source/networking/network_client_manager.c). January proves the buffer and word-sized capacity reset. This is not `MAXIMUM_MESSAGE_SIZE=0xFFF` in the separate Bungie-net message header. A word-aligned array preserves the receiving handler's genuine word-pointer contract. |
| Difficulty | `_game_difficulty_level_easy`, `_game_difficulty_level_normal`, `_game_difficulty_level_hard`, `_game_difficulty_level_impossible` | `0`, `1`, `2`, `3` | [source/game/game.h](../source/game/game.h). Use the enum; a raw ordinal is ambiguous at a call site. |
| Campaign team | `_game_team_default`, `_game_team_player`, `_game_team_human`, `_game_team_covenant`, `_game_team_flood`, `_game_team_sentinel` | `0` through `5` | [source/game/game.h](../source/game/game.h). Multiplayer `_team_red=0`, `_team_blue=1` intentionally overlaps numerically; the field/API determines the namespace. |
| Object types | `_object_type_biped` through `_object_type_sound_scenery` | `0` through `11`; `NUMBER_OF_OBJECT_TYPES=12` | [source/objects/object_types.h](../source/objects/object_types.h). Values are ordinals used by object headers and switch statements. |
| Object masks | `_object_mask_unit`, `_object_mask_item`, `_object_mask_device`, `_object_mask_all` | `0x3`, `0x1C`, `0x380`, `0xFFFFFFFF` | [source/objects/object_types.h](../source/objects/object_types.h). These are already masks; test them against an object type with the established `TEST_FLAG(mask, type)` convention. |
| Object capacity | `MAXIMUM_OBJECTS_PER_MAP` | `2048` | [source/objects/objects.h](../source/objects/objects.h). Capacity is not the current datum count. |
| Rendered-object capacity | `MAXIMUM_RENDERED_OBJECTS` | `256` | [source/objects/objects.h](../source/objects/objects.h). Do not use it to size the master object datum array. |
| UI deletion input-inhibit mask | `WIDGET_DELETED_PLAYER_CONTROL_INHIBIT_FLAGS` | `0x0FFF` | TU-local owner in [source/interface/ui_widget.c](../source/interface/ui_widget.c). Passed as a `word` to `player_control_inhibit_buttons` when a root widget with a non-`NONE` local-player index is deleted. It fences the evidenced low twelve discrete action buttons until each held button is released; it does not cover analog movement/look, and it is not `UNSIGNED_SHORT_MAX`, a universal all-buttons mask, or a bit index for `FLAG`. |
| Markers/regions/attachments | `MAXIMUM_MARKERS_PER_OBJECT`, `MAXIMUM_REGIONS_PER_OBJECT`, `MAXIMUM_NUMBER_OF_ATTACHMENTS_PER_OBJECT` | `64`, `8`, `8` | [source/objects/objects.h](../source/objects/objects.h) and [source/objects/object_definitions.h](../source/objects/object_definitions.h). These belong to different arrays despite sharing an object definition. |
| Damage material modifier capacity | `MAXIMUM_NUMBER_OF_MATERIAL_TYPES` | `40` | [source/game/game_globals.h](../source/game/game_globals.h); used by `damage_definition.material_modifiers` at [source/objects/damage_effect_definitions.h](../source/objects/damage_effect_definitions.h). It is storage capacity, not the current named-material count. |
## Xbox shader encodings (confirmed SDK owners, context-specific)
The existing [Xbox D3D8 types header](../xbox/include/D3D8Types.h) owns the
register-combiner macros and enums. Prefer them to packed hexadecimal shader
words; this documents the actual inputs and outputs without creating new
engine constants. They are hardware encodings, not Bungie identifier recovery.
| Encoding | Existing owner / interpretation | Example |
| --- | --- | --- |
| Four combiner inputs | `PS_COMBINERINPUTS(a,b,c,d)` packs four input bytes | `PS_COMBINERINPUTS(PS_REGISTER_T0, PS_REGISTER_T0, PS_REGISTER_ZERO, PS_REGISTER_ZERO)` is `0x08080000`. |
| Input register/mapping/channel | `PS_REGISTER_*`, `PS_INPUTMAPPING_*`, `PS_CHANNEL_*` | `PS_REGISTER_V0 \| PS_INPUTMAPPING_UNSIGNED_INVERT` is `0x24`; `PS_REGISTER_V1 \| PS_CHANNEL_ALPHA` is `0x15`. These are already encoded masks, not bit indices for `FLAG`. |
| Constant one input | `PS_REGISTER_ONE` | `0x20`, zero register with unsigned-invert mapping; not a literal register number 32. |
| Combiner destinations | `PS_COMBINEROUTPUTS(ab,cd,mux_sum,flags)` | R0/discard/discard/identity is `0xC0`; R1/discard/discard/identity is `0xD0`. |
| Stage count | `PS_COMBINERCOUNT(count,flags)` | Counts 1 and 3 with zero flags encode as 1 and 3. Flags zero select the SDK defaults; count is not a bit index. |
| Texture addressing modes | `PS_TEXTUREMODES(t0,t1,t2,t3)` with `PS_TEXTUREMODES_*` | PROJECT2D/NONE/NONE/NONE encodes as 1. |
The Widgets reconciliation replaces these packed words using the existing
macros; all generated code and non-debug sections remain unchanged. Do not
assign a fixed-function `D3DVSDE_*` semantic name to an arbitrary custom vertex
shader register just because its number agrees: Widgets sends color through
register 9 and texture coordinates through register 4. The custom shader's
input contract, not the fixed-function spelling, determines their meaning.
## Damage flags relevant to the current caller/helper packet
These are confirmed but context-specific. The enum values are **bit indices**;
use `TEST_FLAG`/`SET_FLAG` rather than the numeric masks shown for audit
convenience.
| Storage | Symbol | Bit / mask | Owner |
| --- | --- | --- | --- |
| `being_damaged_flags` | `_object_being_damaged_body_depleted_bit` | `0` / `0x01` | [source/objects/damage.h](../source/objects/damage.h) |
| `being_damaged_flags` | `_object_being_damaged_shield_depleted_bit` | `3` / `0x08` | [source/objects/damage.h](../source/objects/damage.h) |
| `being_damaged_flags` | `_object_being_damaged_by_friendly_bit` | `4` / `0x10` | [source/objects/damage.h](../source/objects/damage.h) |
| `being_damaged_flags` | `_object_being_damaged_multiplied_by_difficulty_bit` | `5` / `0x20` | [source/objects/damage.h](../source/objects/damage.h) |
| `damage_data.flags` | `_damage_no_statistics_bit` | `7` / `0x80` | [source/objects/damage.h](../source/objects/damage.h) |
| `object.damage_flags` | `_object_passed_shield_damage_threshold_bit` | `1` / `0x0002` | [source/objects/objects.h](../source/objects/objects.h) |
| `object.damage_flags` | `_object_shield_depleted_bit` | `3` / `0x0008` | [source/objects/objects.h](../source/objects/objects.h) |
| `object.damage_flags` | `_object_shield_over_charging_bit` | `4` / `0x0010` | [source/objects/objects.h](../source/objects/objects.h) |
| `object.damage_flags` | `_object_cannot_take_damage_bit` | `11` / `0x0800` | [source/objects/objects.h](../source/objects/objects.h) |
Do not merge the two “shield depleted” names: one is an event/result bit in a
32-bit accumulator and the other is persistent object state in a 16-bit
field. Their current numeric bit happens to be the same.
January-specific category, side-effect, material-response, and private-helper
values should remain in the owning Damage enum unless a genuine shared owner
is found. A comparison against `1`, `2`, or `3` in disassembly is not enough
to infer which enum namespace it belongs to. Likewise, the Damage aftermath
upward bias and similar algorithm coefficients are not general math
constants; retain a semantic local expression and document its function-level
provenance instead of exporting a global name.
## Assertions and object-access macros
See [the shared assertion API and Units example](assertions.md). Assertions test
an invariant that must be true; a diagnostic error condition usually needs
negating. `_object_connected_to_map_bit` is bit 11 in `object.flags`, whereas
`_object_header_connected_to_map_bit` is bit 5 in a separate header flags byte.
Do not substitute one for the other.
Use typed accessors such as `unit_get`, `weapon_get`, `object_definition_get`,
`bitmap_group_get`, and `sound_definition_get` from their existing subsystem
headers. Keep raw `tag_get` and object-access casts encapsulated there rather
than repeating casts at each call site.
## Supplied-source crosswalk (evidence, not yet canonical declarations)
The user supplied three CE source files on 2026-09-06. Their hashes, local
locations, target fingerprints, and integration caveats are recorded in the
[source reconstruction map](user_source_reconstruction_map_20260906.md).
The following names occur there; do not assume they are already available
from canonical headers or export TU-private coefficients globally.
| Supplied name / expression | Value | Canonical correspondence and scope |
| --- | --- | --- |
| `RANDOM_A` | `1664525L` / `0x0019660D` | Literal in [source/math/random_math.c](../source/math/random_math.c); LCG multiplier, not a general math constant. |
| `RANDOM_C` | `1013904223L` / `0x3C6EF35F` | Same owner; LCG increment. Seed arithmetic is unsigned 32-bit modulo arithmetic. |
| `RANDOM(seed)` | Update seed, then take its upper 16 bits | Source macro uses a 16-bit right shift; do not substitute low bits or mutate the seed twice. |
| `REAL_RANDOM(seed)` | `(1.f / (real)0xffff) * RANDOM(seed)` | Divisor is `65535`, not `65536`; the source real result can reach both 0 and 1. Integer range scaling instead uses a right shift by 16 and is upper-exclusive for its valid range. Preserve expression order and promotions. |
| `RANDOM_DIRECTION_TABLE_GEOSPHERE_SEGMENT_COUNT` | `16` | RandomMath's geosphere construction argument, not a player capacity or direction-table length. |
| `COUNTDOWN_TIMER_CANNOT_ACCLERATE_AMOUNT` | `MILLISECONDS_PER_SECOND - 1` = `999` | Source spelling includes `ACCLERATE`; canonical server calls it `NETWORK_GAME_MINIMUM_COUNTDOWN_TIME`. |
| `CLIENT_COUNTDOWN_TIMER_MODIFICATION_AMOUNT` | `5 * MILLISECONDS_PER_SECOND` = `5000` | Canonical server `NETWORK_GAME_COUNTDOWN_ADJUSTMENT`. |
| `LAN_INITIAL_SERVER_GAME_COUNTDOWN` | `31 * MILLISECONDS_PER_SECOND - 1` = `30999` | Canonical server `NETWORK_GAME_COUNTDOWN_TIME`. |
| `LOCAL_INITIAL_SERVER_GAME_COUNTDOWN` | `11 * MILLISECONDS_PER_SECOND - 1` = `10999` | Canonical server `NETWORK_GAME_SPLITSCREEN_COUNTDOWN_TIME`. |
| `MAXIMUM_SERVER_GAME_COUNTDOWN` | `31 * MILLISECONDS_PER_SECOND - 1` = `30999` | Separate semantic role from the LAN initial time, despite equal values. |
| `NETWORK_GAME_CLIENT_CONNECTION_PROCESS_TIMEOUT_MILLISECONDS` | Debug `120000`; release `15000` | Supplied client enum selects `120` or `15` times `MILLISECONDS_PER_SECOND` under `DEBUG`; do not use one build's timeout in the other. |
| `NETWORK_GAME_CLIENT_GAME_SEARCH_INTERVAL_MILLISECONDS` | `2000` | Supplied client search cadence, `2 * MILLISECONDS_PER_SECOND`. |
| `NETWORK_GAME_CLIENT_GAME_ADVERTISED_GAME_TIMEOUT` | `6000` | Supplied client: twice the search interval plus `2 * MILLISECONDS_PER_SECOND`; not the connection timeout. |
| `NETWORK_GAME_CLIENT_GAME_PING_INTERVAL_MILLISECONDS` | `1000` | Supplied client ping cadence, `1 * MILLISECONDS_PER_SECOND`. |
| `NETWORK_GAME_CLIENT_INITIAL_PACKET_SEQUENCE_NUMBER` | `1` | Supplied client protocol sequence origin; not a universal first array index. |
Countdown correspondences are in
[source/networking/network_server_manager.c](../source/networking/network_server_manager.c).
The supplied server's client-machine flag indices are connected `0`,
validated `1`, level-loaded `2`, and precached `3`. Canonical currently uses
joined/loaded names for indices 1/2; numeric agreement alone does not authorize
a global semantic rename. These flags are not object-datum flags.
The supplied RandomMath also references `_full_circle`, `_half_circle`, and
`_quarter_circle`, but their defining header was not supplied and they are not
present in the current canonical math headers. Do not invent a declaration
or infer exact expression ordering solely from those names.
The supplied client names its two flag indices
`_network_game_client_connected_to_server_bit = 0` and
`_network_game_client_sent_join_request_to_server_bit = 1`. Canonical uses
shorter inferred names and literal timing values; restore names only with
ordinary strict-object and consumer checks, not by unreviewed bulk replacement.
## AI profile meter layout
January's shared AI profiling state is declared in
`source/ai/ai_profile.h`. Include that owner when incrementing a meter; do not
declare an opaque `extern byte ai_profile[]`, a partial local structure, or a
consumer-specific view of the same global.
- `ai_profile.meters` begins at offset `0x0C`.
- There are 28 meter records, each `0x88` bytes.
- `accumulator` is at record offset `0`; `current_value` is at offset `2`.
- A meter accumulator is therefore at `0x0C + index * 0x88`.
Frequently used indices and accumulator offsets:
| meter enum | index | offset |
| --- | ---: | ---: |
| `_ai_meter_encounters_updated` | 1 | `0x094` |
| `_ai_meter_actors_updated` | 3 | `0x1A4` |
| `_ai_meter_actors_active` | 4 | `0x22C` |
| `_ai_meter_units_updated` | 6 | `0x33C` |
| `_ai_meter_units_active` | 7 | `0x3C4` |
| `_ai_meter_dead_props_acknowledged` | 9 | `0x4D4` |
| `_ai_meter_dead_props_orphaned` | 10 | `0x55C` |
| `_ai_meter_dead_props_unacknowledged` | 11 | `0x5E4` |
| `_ai_meter_enemy_props_acknowledged` | 12 | `0x66C` |
| `_ai_meter_enemy_props_orphaned` | 13 | `0x6F4` |
| `_ai_meter_enemy_props_unacknowledged` | 14 | `0x77C` |
| `_ai_meter_friendly_props_acknowledged` | 15 | `0x804` |
| `_ai_meter_friendly_props_orphaned` | 16 | `0x88C` |
| `_ai_meter_friendly_props_unacknowledged` | 17 | `0x914` |
| `_ai_meter_collisions` | 21 | `0xB34` |
| `_ai_meter_line_of_sight` | 22 | `0xBBC` |
| `_ai_meter_line_of_fire` | 23 | `0xC44` |
| `_ai_meter_action_change` | 26 | `0xDDC` |
The leading booleans are independently exposed through the Halo Script
global table, which authenticates their names and offsets: `disabled` at
`0x00`, `move_actors_randomly` at `0x01`, `show` through
`show_sound_distance` at `0x04` through `0x0B`. The complete global is
`0xEEC` bytes.
## Reconstruction checklist
1. Search for an existing owner before adding a constant, enum, macro, or
typedef. Include that owner instead of redeclaring it locally when header
placement is safe.
2. Establish the storage type and namespace before interpreting `-1`, `0`, or
a bit number. `NONE`, `NULL`, a tag index, and a byte terminator are not
interchangeable merely because some bytes match.
3. Compare exact float bytes and x87 predicates before replacing a literal
with `_real_epsilon`, `MIN`, `MAX`, or `PIN`. Finite agreement does not
prove NaN, infinity, tangent, or signed-zero agreement.
4. Treat enum `_..._bit` members as bit positions and `_..._mask...` members
as masks. Keep the owning field width; do not widen a byte/word flag field
to `long *` for macro convenience.
5. Treat every maximum as a capacity for its named owner, not a current count
or a substitute capacity for a nearby array.
6. Preserve public type widths at ABI boundaries. In particular, `real` is
float and `boolean` is byte even when i386 register or stack mechanics make
a mismatched declaration appear to work.
## Leaf Map construction constants (context-specific)
These values are authenticated by January's Leaf Map strings, data, control
flow, and the reconciled private caller graph. They belong to
`source/structures/leaf_map.c`; they are not general geometry constants.
| Role | Source spelling / value | Boundary |
| --- | --- | --- |
| BSP traversal stack capacity | `MAXIMUM_NODE_STACK_COUNT = 256` | Assertion text names the constant and aggregate field. Stack entries are signed node designators whose sign bit records the child side. |
| Initial face polygon capacity | `MAXIMUM_LEAF_FACE_VERTICES = 64` | Bounds the local clipped polygon and the correctly owned Geometry clip call. |
| Portal intersection capacity | `MAXIMUM_PORTAL_VERTICES = 64` | Bounds the temporary intersection polygon; it is not a generic tag-block capacity. |
| Initial projected world square | coordinates `+/-1536.f` | The four-point polygon is clipped by every ancestor BSP plane; do not export 1536 as a world-wide coordinate bound. |
| Leaf-face clip epsilon | `0.000244140625f` (`1/4096`) | Algorithm-specific clip tolerance, distinct from `_real_epsilon`. |
| Portal intersection epsilon | `0.00048828125f` (`1/2048`) | Algorithm-specific intersection tolerance. |
| Portal closure thresholds | area `< 0.0025f` or `sqrt(area)/perimeter < 0.01f` | Preserve expression order and the perimeter call; these are not generic degenerate-polygon rules. |
See [the Leaf Map reconciliation ledger](object_matching_logs/leaf_map_obj_private_graph_reconciliation_20260909.md)
for symbol provenance, strict-credit boundaries, and the remaining data/BSS
gap.
## Rasterizer shared-state lookup
Use `source/rasterizer/rasterizer_frame_statistics.h` for the complete January
**0x170-byte** frame-statistics record and its external declaration. Do not
introduce another consumer-specific prefix. Dynamic buffer counts are not
maximum capacities; model skinning/lighting accumulators count uploaded
constant bytes, not objects. See the
[shared counter owner ledger](object_matching_logs/rasterizer_frame_statistics_obj_shared_counter_owner_20260906.md)
for the current writer-backed fields and intentionally unreviewed interiors.
Use `source/rasterizer/rasterizer_debug_options.h` for the genuine January
debug-options record; do not add another TU-local prefix. The size is **0x8C**
(not later HCEA's 0x90). Active Camouflage flags are at 0x41/0x42; January
`zbias` at 0x54 is `long`, not `real`; `pad3_scale` is at 0x6C and the six
debug reals start at 0x70. The pixel-counter active/enabled pair is 0x88/0x89.
Unknown bytes must not inherit later-build names merely by offset.
The complete 0xF0 Xbox `pixel_shader_definition` is in
`source/rasterizer/xbox/rasterizer_xbox_pixel_shader.h`. Use this associated
header when a complete type is needed; the broad Xbox header retains its
forward declaration to avoid colliding with legacy local definitions.
See the [shared-owner/cache admission](object_matching_logs/rasterizer_xbox_active_camouflage_obj_shared_owner_cache_20260906.md)
for provenance, the typed initializer and pinned-compiler layout tests.
### PlayerProfile and S3TC constants
January; 2026-09-07 owner reconciliation:
- `source/saved games/player_profile.h` owns the 48-byte profile: name is
12 wchar_t at +0, color short at +24, flags word at +26, ten byte map flags
at +28, last level short at +38 and eight-byte controller settings at +40.
These are not the larger later-PC/PDB profile layout.
- `SAVED_GAME_FILE_BLOCK_SIZE` is 512 in saved_game_files.h. PlayerProfile's
typed file block has 48 profile bytes, a 20-byte XDK signature and 444 bytes
of zeroed padding. Do not serialize uninitialized padding for matching.
- `S3TC_BLOCK_PIXELS` is 16 (4 by 4); single-pixel coordinates are 0..3.
RGB565 is blue/green/red 5/6/5 in the project's byte-channel order.
S3TC's weights are 0.082f, 0.6094f, 0.3086f in that order; maps are
RGB4 {0,2,3,1}, RGB3 {0,2,1,3}. These are codec operations, not flag bits.
- See [the coherent batch ledger](object_matching_logs/player_profile_s3tc_batch_reconciliation_20260907.md)
for evidence limits, safe repairs, retained fuzzy work and exact debits.
Useful corroborating ledgers:
- [docs/object_matching_logs/damage_obj_object_damage_update_20260904.md](object_matching_logs/damage_obj_object_damage_update_20260904.md)
documents project `real`, enum, flag, tick, and clamp use in a verified
Damage owner.
- [docs/object_matching_logs/bipeds_obj_jonas_disconnect_bsp_20260827.md](object_matching_logs/bipeds_obj_jonas_disconnect_bsp_20260827.md)
records the January all-bits-set `NONE` representation in real object
fields.
- [docs/object_matching_logs/collision_bsp_obj_sphere_cluster_20260904.md](object_matching_logs/collision_bsp_obj_sphere_cluster_20260904.md)
records a specifically authenticated byte-buffer/bit-vector boundary; it is
an exception requiring evidence, not a blanket license for representation
casts.
-35
View File
@@ -1,35 +0,0 @@
# January COMMON pool: ordering hypothesis and ownership limits
Lane C research, 2026-09-22; corrected during canonical reconciliation.
`linker_common.obj` is a synthetic split of tentative COMMON definitions.
Several independently attributed records appear in contiguous clusters whose
positions correlate with module order. This is useful as a **negative
constraint**, but it does not prove the owner of an individual symbol.
Every pooled record in `config/contribs.json` resolves to synthetic module 847.
The HCEX PDB lists these globals without a compiland. Declaration order and
first-reference order do not reliably reproduce the pool order, either.
Consequently, do not create a tentative definition merely because the symbol
falls between two apparently owned records.
The original Lane C packet applied that inference too strongly. Canonical
reconciliation excluded its 116-definition COMMON wave. An existing owner test
reads January's split `rasterizer_frame_statistics` object, where its eponymous
global appears only as an UNDEF external. That does **not** prove an import:
csplit renders a pooled COMMON record as UNDEF (value 0) in a defining TU and in
an importing TU alike, so the test cannot tell them apart and the owner of
`_rasterizer_frame_statistics` remains **undetermined** (correction recorded
2026-09-26; the test itself and its ownership checks are unchanged). The pool-order
hypothesis does not settle it either. The same lack of
independent proof affects `hs_syntax_data`, `ai_globals`, and
`rasterizer_model_cortana_hack`; their Lane C moves were also not integrated.
These are unresolved ownership questions, not credited reconstructions.
The useful, narrower rule remains: a genuine tentative definition must be
uninitialised in source (`struct foo bar;`), while `= 0` or `= NULL` gives the
symbol different COFF ownership. Compare both the January object and the
rebuilt object's symbol class before accepting a declaration. The read-only
`tools/campaign/linker_common_base.py` can inspect the known pool records, but
its partial base earns **zero** matching credit under the all-or-nothing data
metric. Its output must not be used as an ownership oracle.
-171
View File
@@ -1,171 +0,0 @@
# H1 tags cross-build intake (2026-09-06)
## Status and authority
The user supplied `C:/Users/isabe/Downloads/haloleak2024.rar`, specifically
`halo1/h1_tags`. Five explicitly selected executables were extracted for local
analysis. No executable was run and no tag assets were extracted. These inputs
are **CROSSBUILD_ONLY** evidence, not the January 2002 Xbox target.
The user subsequently supplied the extracted folder
`C:/Users/isabe/Downloads/haloleak2024/halo1/h1_tags`. All five symbol-executable
SHA-256 hashes match the archive copies below exactly, so the saved analysis is
reusable. The extracted `halo1` subtree also contains no PDB/DBG files at this
checkpoint. The source folder remains read-only.
All five PE timestamps report 2020-11-23; their CodeView paths identify a
`build/v140/Win32` build. A timestamp is a claim in the file, not independent
build provenance. Each image has zero COFF symbols and zero exports. The
CodeView records are **RSDS references to external PDBs**, not embedded PDB
contents. The full archive listing contained 211,474 entries, no `.pdb` or
`.dbg` files, and three `.map` files that are Halo 2 game assets rather than
linker maps. Do not describe the `_symbols.exe` suffix as proof of a complete
debug-symbol payload.
## Exact input identities
| Executable | Bytes | SHA-256 |
| --- | ---: | --- |
| `guerilla_symbols.exe` | 9,764,864 | `44538c3effe5a740f6995e7f6867995e81ec7d32ee8bb0bb74f04c2b9c203b83` |
| `halo_cache_symbols.exe` | 6,672,896 | `740869688354defd295e28adf94bd4ea41385e9d4a98dc08764515285cf05b55` |
| `halo_tag_symbols.exe` | 7,450,624 | `c5f58fe29cdde668d0e30f6848ad50b8f49a28e99c30de2b1bb75cf7bbfb0712` |
| `sapien_symbols.exe` | 11,303,424 | `b1f0b207f5f49cb0ce90f5be7957197d3be612593ff52e7a50fc17e2b88a4828` |
| `tool_symbols.exe` | 10,631,680 | `079d20d7792e9565b24ab2cff8115f76a6e48749a8e8ffd190f57954495f0a97` |
The missing PDBs must match **both GUID and age**, not just basename:
| PDB basename | GUID | Age (decimal) |
| --- | --- | ---: |
| `guerilla_symbols.pdb` | `9b751e93-d090-4fbf-9a7c-d8e3cbcb1866` | 125 |
| `halo_cache_symbols.pdb` | `7f03f998-b7b0-4ea6-b18f-a6506e3442e5` | 14 |
| `halo_tag_symbols.pdb` | `eac025b6-d3f8-4ae9-a47e-9882d7c58906` | 107 |
| `sapien_symbols.pdb` | `82b677b3-b983-4c49-912e-ae5641ce5f76` | 87 |
| `tool_symbols.pdb` | `3e2e627d-3e3b-4396-afd6-8ded91e9d954` | 80 |
## Local evidence and tools
Research root:
`C:/Users/isabe/Documents/Codex/2026-07-13/i-w/research/symbol-build-h1-tags-20260906`
- `manifest.json`: PE sections, debug-directory records, exact PDB paths,
identities, and explicit limits.
- `inspect_builds.py`: regenerates the manifest using `pefile`, without
executing or modifying the five input images.
- `ghidra-scripts/CrossBuildInventory.java`: exports observed function names,
their Ghidra source classification, and body ranges. It performs no renames.
- `ghidra-projects/h1-tags-crossbuild.gpr`: new, separate cross-build project.
- `ghidra-import.log`: successful automatic analysis and save of
`halo_cache_symbols.exe` (187 seconds, no timeout).
- `ghidra-import-remaining-retry.log`: the other four executables were imported
and saved with `-noanalysis`. The already-imported cache executable was
rejected as a duplicate and was **not overwritten**.
- `ghidra-export-explicit-bundle.log` and
`ghidra-export-cache-verified.log`: successful read-only inventory exports.
Earlier export attempts failed independently of the saved imports. The
explicit `ghidra-scripts/META-INF/MANIFEST.MF` and a subsequent run resolved
script-bundle loading. The installed headless README names an unavailable
timeout method; the installed Java source calls it `analysisTimeoutOccurred`.
The inventory script now uses plain `GhidraScript`; analysis completion is
established by the analysis log, not by a no-analysis export run.
Verified original-project status at the 2026-09-06 handoff (see the separate
2026-09-07 analysis update below):
| Program | Analysis status | Observed function records |
| --- | --- | ---: |
| `halo_cache_symbols.exe` | Automatic analysis completed | 17,858 |
| `halo_tag_symbols.exe` | Imported only; analysis pending | 18 |
| `guerilla_symbols.exe` | Imported only; analysis pending | 5 |
| `sapien_symbols.exe` | Imported only; analysis pending | 11 |
| `tool_symbols.exe` | Imported only; analysis pending | 32 |
Function inventories are `<executable>.functions.tsv`. These counts include
analysis-created functions/thunks and imports, **not recovered original Halo
function counts**. Preserve each row's source classification and do not count
names lacking a `FUN_` prefix as original symbols.
For the analyzed cache executable, 17,525 records have Ghidra source `DEFAULT`,
324 have `ANALYSIS`, and nine have `IMPORTED`; none establishes missing PDB
contents. The `.gpr` file is a zero-byte project marker; its sibling `.rep`
directory holds the databases and must stay with it.
The existing Ghidra installation was found by following its saved application
log, not by assuming it was on PATH:
`C:/Users/isabe/Documents/Codex/halo-tools/apps/ghidra_12.1.2_PUBLIC`.
Its configured JDK is the sibling `jdk-21.0.11+10` directory. The live RPC
endpoint at `127.0.0.1:18081` was offline at intake; the headless launcher is
usable independently. Only Ghidra's local settings/cache directories needed
additional write permission; the new project and outputs stay in the workspace.
IDA has not been located in the available tools, PATH, ordinary installation
directories, registry installation entries, or the local Halo tool bundle.
**The user's requirement to use both IDA and Ghidra remains outstanding.** Do
not claim that Ghidra, Capstone, or a PE parser satisfies the IDA half. A usable
installation/license or further location evidence is needed. Do not upload
these inputs to a cloud decompiler or symbol service without authorization.
Keep the executables, databases, and generated bulk inventories local. This
ledger records pointers and hashes only; it is not authorization to publish
the supplied binary corpus.
## Reconstruction plan and limits
1. Identify the source build and validate any future PDB against the recorded
GUID/age before loading its names/types into its own donor database.
2. Preserve evidence categories: imported API names, disassembler inference,
diagnostic literals, genuine debug records, and independently January-proven
facts are different things. A generated `FUN_...` name is not an original
name and must not become a production `code_<address>` substitute.
3. Use diagnostic strings, callers, data references, and instruction/CFG
comparisons to propose January correspondences. Verify each mapping locally;
never add one global address delta across builds.
4. "Un-inline" means recover and annotate candidate helper operations and their
call-site relationships where evidence permits. It cannot recreate all lost
source, local variables, or original inline boundaries. Do not rewrite the
January target, force new calls, or emit a `point_from_line3d` COMDAT to make
the donor's decompilation look tidier.
5. Names and types can accelerate source reconstruction, but exact credit still
requires the January COFF byte/relocation, source-authenticity, whole-TU,
whole-tree regression, and object-admission gates. No canonical code bytes
are earned by this intake or by automatic analysis alone.
## 2026-09-07: seven further programs analyzed and saved
The original project/cache analysis above remains preserved. A separate project
now holds the four remaining Symbols programs and the three supplied Play
programs:
`C:/Users/isabe/Documents/Codex/2026-07-13/i-w/research/hs7/remaining-projects/h1-tags-remaining-seven.gpr`.
Keep its sibling `.rep` directory with it. No supplied executable was run.
All seven Programs were saved and 21 detailed exports were produced. Input
hashes and exported PDB GUID/ages were rechecked. The bounded analysis phases
finished with warnings for Halo Tag Symbols (19,228 function records), Sapien
Symbols (54,820), Guerilla Play (3,770), Sapien Play (9,045) and Tool Play
(7,893). Guerilla Symbols (41,854) and Tool Symbols (46,630) reached their
300-second limits and remain explicitly **partial**, not complete analyses.
The local guide is `research/hs7/ANALYSIS_GUIDE.md` under the outer workspace;
the detailed functions/types/metadata exports are in `research/hs7/analyze-detailed`.
`research/hs7/remaining-analysis-summary.json` has SHA-256
`dbe1283d88ae4b8839b42888e7f80d70be4a87233824a99f7c6a77540bae5aaa`.
The guide records source classifications, timeout evidence, API-type archive
provenance and analyzer warnings, including WindowsResourceReference's OSGi
warning. PDB analyzers were disabled because matching PDBs are absent; no
external symbol download was performed. These generated records are not
restored original names, inline boundaries or debug assets, and earn no exact
code credit. IDA remains unlocated and that part of the request is outstanding.
## Related networking evidence
[Exploring The Halo 1 System Link Protocol](https://hllmn.net/blog/2023-09-18_h1x-net/)
provides retail PAL packet/debugger evidence. Its decoded advertisement retains
the suffix `age in a bot`. This corroborates, but does not independently prove
for January, our source-attested join-token implementation: the first four
bytes are transformed while the remaining twelve survive. The article's
two-second search cadence is another comparison lead. Its PAL timing and
absolute addresses must not replace January constants or addresses.
Use the article's emulator-boundary inspection method as a future local
runtime-validation aid. No live interception or traffic manipulation was
performed for this intake. The canonical token implementation and runtime tests
are documented in
`docs/object_matching_logs/network_server_manager_obj_source_attested_packet_20260906.md`.
-374
View File
@@ -1,374 +0,0 @@
# Exact-match acceleration playbook
This document records techniques with broader application to the January 2002
Halo Xbox decompilation. Object-specific measurements remain in
`docs/object_matching_logs/`; this file contains only controls, triage rules,
and campaign practices that have survived strict comparison.
The objective is faster reconstruction without lowering the bar. A candidate
is not exact because it is correct, similar, the same size, or 99% fuzzy. The
admission evidence remains XDK 3911 output, exact normalized bytes, exact
relocation shape and resolved destinations, exact owned data/layout, and a
whole-translation-unit regression audit.
## Campaign strategy
1. **Finish objects, not isolated functions.** Prefer a unit with a bounded
residual and known data ownership over a larger number of unrelated leaves.
Once an object is opened, carry it through code, data, ownership, regression,
documentation, and `Matching` admission unless a measured compiler tie
satisfies the parking standard.
2. **Prioritize reusable foundations.** Scenario/BSP accessors, real-math
leaves, player/unit layouts, tag/object accessors, datum iterators, compiler
wrapper families, and complete donor translation units reduce later work.
3. **Separate reconstruction from tuning.** First recover the January
semantics, types, call graph, private ABI, and owned data. Tune source shape
only after target size/relocation evidence says the structure is close.
4. **Work atomic private-ABI clusters together.** Static helpers and their
callers can form a register-allocation fixed point. Splitting them across
lanes produces false blockers.
5. **Use a bounded experiment budget.** Test one source factor at a time,
record the exact result, and stop repeating compiler-equivalent spellings.
A parked result needs explicit size, relocation, first-divergence, and
exhausted-control evidence.
6. **Automate candidate generation, never acceptance.** Shape-transfer and
donor tools may propose source, but the unchanged strict comparator and
regression gates decide admission.
### Select closeouts from strict evidence, not the ordinary percentage
Run `python -m tools.rank_object_closeouts` after regenerating the ordinary
and semantic reports. The ranking deliberately combines:
- the complete function inventory from the ordinary report;
- the reviewed strict `accepted_ledger`;
- object `Matching` status; and
- the evidence-backed parked-function catalog.
This prevents three recurring campaign mistakes:
1. treating a differently numbered local jump-table label as unfinished code;
2. repeatedly dispatching a one-function object whose only residual is already
a proven compiler tie; and
3. overlooking a data-only closeout after every function is exact.
The tool is a triage aid, not an admission oracle. `NEAR_CLOSEOUT` means the
remaining work is unparked and bounded; it does not predict that source tuning
will succeed. `LEDGER_REVIEW` means earlier object work exists and its
do-not-repeat/reopen rules must be read before dispatch. `PROVEN_BLOCKED` means
every remaining function is in the
reviewed parked ledger and must not be reopened without satisfying its stated
reopen criterion. Always read the object's Markdown ledger before editing.
## Evidence hierarchy
From strongest to weakest:
1. January target bytes, relocation metadata, linked addresses, and section
ownership.
2. January sibling functions and exact donor translation units.
3. January assert strings, source paths, help tables, tag definitions, and call
sites.
4. XDK 3911 headers and synthetic compiler experiments under the exact flags.
5. October Xbox, Halo PC/CEA/HCEX PDBs, community forks, and later-engine types.
Levels 4 and 5 generate hypotheses. They do not override January evidence.
Every imported field, enum, prototype, or name must be corroborated locally.
## High-yield source controls
### Ownership and alias modeling
VC7 code generation depends on what it can prove about globals:
- Aggregate storage can force address common-subexpression elimination or
destroy an index because sibling fields may alias.
- Separate globals can preserve a live SIB index and eliminate reloads.
- A one-field structure can retain field-oriented optimizer behavior while a
separate array supplies non-aliasing proof. This closed
`game_engine_multiplayer_sounds.obj`: it preserved `dec; mov; je` flags in
the update while producing SIB stores in the queue helper.
- Static versus external linkage affects alias knowledge, section order,
symbol storage class, and whether zero-initialized data becomes `.bss` or a
tentative/common symbol.
Always verify the full object. A linkage change can leave every function exact
while silently deleting or reordering `.bss`.
### Statement and branch order
- VC7 often reflects the original failure-first versus success-first topology.
Reversing an `if` can fix multiple relocation-address regions at once.
- Tail-merging is sensitive to deliberately asymmetric source duplication.
If two switch cases must share a forward machine-code tail but an explicit
shared label sinks a callee-save or changes the prologue, keep the simple
early path's tiny terminal test locally and route the longer path forward to
the other case's copy. In `shaders.obj`, VC7 cross-jumped that readable
duplicate into one shared target tail while preserving the required ESI
lifetime. Verify relocation identities: this is a measured block-placement
control, not permission to duplicate arbitrary logic.
- Common stores in two branches may be tail-merged, but a constant-valued
branch can instead materialize an immediate store and grow the function.
- Comparison polarity matters even when predicates are logically equivalent;
it controls `cmp` operand order, signed branches, and register lifetimes.
- Use a single return when the target structure permits it. When the exact
source requires multiple source returns to reproduce a common compiler
epilogue, record that as an evidence-backed house-style exception.
### Scalar stores versus aggregate operations
- Field-wise stores give the scheduler independent operations and can reproduce
interleavings that `{0}`, aggregate assignment, `memset`, or `rep stos`
cannot.
- Conversely, aggregate assignment is useful when the target uses `rep movs`
or a single calculated element address.
- VC7 groups local-struct zero initialization by brace level. If member zero
is itself an aggregate, `= {0}` can produce one whole-struct zero live range,
while `= {{0}}` can split the first member's fill from the implicit trailing
fill. A target whose zero stores split exactly at that member boundary is
evidence for testing the extra brace; this closed
`telnet_console_initialize` without changing behavior.
- Preserve target load and store order before trying declaration-order noise.
### Local lifetime is more important than local spelling
Naming an expression does not necessarily change VC7 output; the optimizer can
fold a short-lived alias immediately. What matters is where the value becomes
live and how long it must survive. In `data_packets.obj`, naming
`decoded_data + sizeof(short)` beside its final memory call did nothing across
several experiments. Computing the same readable payload pointer *before* the
range assert and keeping it live across validation plus an intervening integer
encode changed no instruction by itself, but rotated three later scratch
registers and closed a 768-byte function exactly.
In plain English: do not keep renaming a value and expect different code. Move
the point where the program genuinely needs to remember it, then measure the
whole function. This is especially useful around asserts and calls, where an
additional live pointer changes the compiler's interference graph without
inventing operations.
### Integer width and signedness
- `movsx`/`movzx`, partial-register operations, and signed branch opcodes reveal
the real promotion boundaries.
- Function parameter widths matter even when the x86 ABI still occupies a
four-byte stack slot. In `rasterizer_xbox_motion_sensor.obj`, changing a
render-target parameter from `long` to the proven `word` transformed
`movzx` into the target's `xor reg, reg; mov reg16, [memory]` sequence and
closed the entire remaining half of a 1,920-byte function.
- Change signedness only where the target proves it. A `jle` versus `jbe`
difference is often a type error; an ESI/EDI mirror is not.
- Recover narrow accumulators instruction by instruction rather than relying
on C's default integer promotions.
### x87 controls
- A value live after a store selects `fst`; a dead value selects `fstp`.
- Source operand order controls non-commutative subtraction/division memory
forms. VC7 canonicalizes commutative addition/multiplication; spelling order
is not a lever there.
- Passing, returning, or globally storing a floating result produces distinct
stack-staging patterns.
- Never substitute mathematically equivalent expressions that change signed
zero, NaN payload/sign, intermediate precision, or rounding.
- When a shared inline vector helper matches standalone but changes scheduling
at one caller, preserve the donor's **expression surface**, including
apparently redundant parentheses. In `collision_features.obj`, changing
`(v->i*t) + p->x` to the equally readable `v->i*t + p->x` altered inline IR
without changing the helper's standalone meaning and restored a January
`fstp`/address-formation boundary. Treat this as a measured inline-context
lever, not a license for blind parenthesis permutations.
- VC7 reassociates a one-expression commutative dot product, so merely reversing
its written terms may do nothing. If January proves a specific x87 load/add
order, sequential accumulation can preserve it. The exact
`collision_prism_test_vector` form initializes the `k` product, then adds
`j`, then `i`; every intermediate remains a normal `real` value.
### XDK inline wrappers
Stock XDK `D3DINLINE` under this compiler emits the January out-of-line D3D
wrapper families when called normally. Address-taking forces a different
standard-ABI body, and redefining `D3DINLINE` as plain `static` can emit a
thunk. Prefer the stock header form and verify every emitted wrapper in the TU.
One motion-sensor translation unit instantiated ten such wrappers exactly in a
single build, including both 432-byte and 544-byte dispatchers. Treat the
header recipe as a high-yield candidate generator, but still compare every
wrapper and its relocation destinations independently.
### Diagnose the first true divergence
- Ignore downstream branch displacements and relocation-address drift until
the earliest semantic or instruction-length difference is closed.
- Decode cdecl pushes right-to-left before blaming register allocation. A wrong
argument order can make a large suffix look like a scheduler problem.
- If a small type correction makes a long suffix exact automatically, the
suffix was displacement drift, not a collection of independent problems.
- Zero-byte intrinsics such as `_ReadWriteBarrier()` are measured controls, not
universal fixes. They can prevent desirable argument hoisting and make an
otherwise close function worse.
- A one-byte `push eax` where the reconstruction emits the two-byte `push 0`
after testing a result is evidence that the original passes the tested
result, not a literal. Preserving that value into the error arm can change a
large register-allocation suffix even when alignment leaves the padded size
unchanged. For size-exact functions containing a switch, inspect padding
and jump-table tails as well as the nominal body.
### Recover the type before tuning the schedule
A value that stays live in a register longer than expected is often evidence
of a wrong prototype or field width, not a mysterious compiler decision.
Before trying declaration orders, barriers, or control-flow rewrites:
- Check what the target returns in `EAX` or `AL`. If the target preserves a
meaningful value until `ret`, a function currently declared `void` may
actually return that value.
- Check whether the target consistently reads, masks, or writes only the low
byte or low word. A `char`, `boolean`, `short`, or `word` may be the real
type even when a 32-bit placeholder produces correct gameplay behavior.
- Prefer the smallest type that is supported by all call sites and target
instructions. Do not narrow a type merely because it happens to improve the
bytes.
In `texture_page.obj`, recovering `spacing_mask` as a 16-bit `word` made a
736-byte packing function exact. Recovering
`texture_page_textures_end` as a `boolean` function explained why January kept
the packing result in `AL` across the loop and made its remaining 112 bytes
exact. In plain English: the compiler was not being stubborn; we had told it
the wrong facts about the program.
### Private ABI controls
A source-static, non-address-taken, frameless helper can receive arguments in
ECX/EDX or another caller-derived convention. External linkage, address-taking,
or a stack frame can destroy that convention. Work from a forced-register
caller outward; do not tune a private helper in isolation.
### Compiler provenance before compiler hunting
Do not attribute a stubborn register or scheduling residual to an unknown
compiler patch until the target's own debug records have been checked.
PDB 2.00 module streams can contain `S_COMPILE2_ST` records with independent
front-end and back-end version triples. For the January `players.obj`, the
record proves `13.0.9254` for both halves, matching the campaign compiler's
reported build.
Use this order:
1. Decode the target compiland's compiler record.
2. Compile the unchanged TU once with each genuinely distinct local compiler
binary.
3. Compare normalized hashes for every residual, not just the first
instruction.
4. If all variants are byte-identical, stop the compiler hunt and return to
source provenance or a demonstrated legal-C control.
For Players, VC7 `13.00.9210` and `13.00.9254.1` emitted byte-identical
candidate hashes for all four residuals. That falsifies the available
patch-level lever and prevents repeated SDK downloads or speculative flag
changes.
## Transfer and donor rules
- A donor is usable only when its source text is resolved and its target
machine shape is exact.
- Derive every recipient callee/global substitution from the recipient target
relocations, never by name similarity.
- Equal relocation-masked bytes can hide a wrong-callee wrapper. Destination
identity and addends remain strict.
- Preserve target statement order; relocation sequence is often its strongest
surviving proof.
- Reject stubs, unresolved donors, inconsistent substitution maps, prologue
mismatches, and data-ownership changes automatically.
### Candidate-only helper COMDATs
An inlined helper can occasionally leave an unreferenced out-of-line COMDAT
copy in the candidate object. Never dismiss it just because the target
function compares equal. It is acceptable only after all of the following are
proved:
- the extra section is a discardable COMDAT with the expected selection rule;
- no relocation in the object refers to it;
- every target-owned function and data section remains strict-exact;
- the ordinary link actually discards it; and
- the consolidated link, semantic audit, and progress report remain clean.
This is a linker-level representation difference, not permission to ignore
extra ordinary code or data.
## Parking standard
Park only when:
- target and candidate padded size are exact;
- relocation count, types, and destinations are exact, or a reviewed
representation-only difference has independently resolved destinations;
- the remaining divergence is classified precisely;
- legal-C controls appropriate to that class have been exhausted;
- the object ledger records a reproducible first divergence and reopen
criterion.
A 92% function with different size or relocations is structural work, not a
compiler tie. A one-instruction scheduler swap can be parked only after the
dependency and source-control search are documented.
## Admission checklist
Before marking an object `Matching`:
1. Build with XDK 3911 and the repository flags.
2. Compare every function with the hardened COFF comparator.
3. Verify every owned `.data`, `.rdata`, and `.bss` span, including symbol
offsets, storage classes, and relocation destinations.
4. Confirm the ordinary report does not hide a strict false positive.
5. Run the whole-TU regression gate against a pre-edit snapshot. Review every
intentional ownership transition; never suppress unrelated failures.
6. Run the consolidated build, semantic audit, and progress report.
7. Update the object ledger with retained and rejected experiments.
8. Mark `Matching` only after all evidence passes.
### Greenfield regression gates
An empty skeleton has no previously exact code or owned data for a pre-edit
manifest to protect. A correct reconstruction will therefore appear to the
regression gate as a set of new functions, symbols, COMDATs, and data. Do not
force that report green and do not waive its findings:
- confirm that it reports no changed previously exact function or section;
- classify every addition against the January target;
- admit the object only after strict function, relocation, ownership,
consolidated-build, and semantic-progress checks pass;
- immediately capture a post-admission manifest and verify it is stable, so
later edits are checked against the newly completed object.
## Original bugs
Exact reconstruction preserves proven original behavior. Mark a defect
`BUG (original)` only when January evidence proves it. In the matching source,
retain the original behavior and add a concise comment describing a safe
corrected-build alternative. Do not silently repair it and do not label our
own reconstruction mistake as an original bug.
An original uninitialized read is an exceptional case, not a general codegen
lever. Preserve one only when the January control flow proves the read, an
independent build or source lineage corroborates it, and a bounded search shows
that defined readable-C alternatives do not match. Record the safe initialized
form and its measured result in the object log. Never introduce a new
uninitialized value merely because it improves register allocation.
Do not infer a fallback assignment merely because target code reloads a value
after a fatal assertion or `system_exit`. In `data_decode_memory`, that
seemingly sensible repair was the sole cause of a whole-function register
allocation mismatch; the January/October machine code plus an exact-unique
HCEA PDB local record and HCEA PPC body showed that the original impossible
default arm left its sole size local unassigned. Remove such a repair only
with the full original-bug evidence gate above, then state the safe corrected
alternative in source and measure it in the object log.
## Prohibited shortcuts
No inline assembly, volatile byte forcing, undefined aliasing, object-byte
patches, compiler pragmas used only to force bytes, flag changes, fuzzy-only
credit, or invented symbols/types. Compiler experiments belong in disposable
worktrees; production admission must remain readable evidence-backed C.
-50
View File
@@ -1,50 +0,0 @@
# Fake-match source review
`tools/fake_match_scan.py` finds unusual C/C++ constructs that deserve a
credibility or semantics review. It is a heuristic lead generator, not an
object comparator, admission gate, or proof that source is fake. A byte match
does not make nonsensical source credible, and a scanner hit does not make
ordinary source wrong.
These examples run from the repository root (the script also derives the
repository root correctly when invoked by an absolute path from elsewhere):
```text
python tools/fake_match_scan.py
python tools/fake_match_scan.py source/ai/actions.c source/game
python tools/fake_match_scan.py --format json --fail-on-findings source/ai
python tools/fake_match_scan.py --list-rules
```
The default input is the repository's `source/` directory. Explicit file and
directory inputs are de-duplicated and emitted in deterministic path/line/rule
order. Known vendored libtiff, zlib, `third_party`, and `vendor` trees are
skipped unless `--include-vendored` is supplied. Comments and string/character
literals are masked before scanning so examples and assert text do not become
findings.
Exit status is deterministic:
- `0`: the scan completed; findings may be present.
- `1`: findings are present and `--fail-on-findings` was requested.
- `2`: an input is invalid or a source file cannot be read.
Every finding should prompt a source review, not an automatic rewrite. Check
whether the construct expresses plausible original program intent, whether the
logic is meaningful for all inputs, and whether first-party source, names,
target CFG/relocations, or a credible contemporary codebase supports it. Re-run
the strict object and regression gates after any resulting edit. Exact bytes
alone are not provenance.
Semantic validity is an admission requirement even when every byte matches.
Reject code whose behavior is nonsensical, whose conditions or data flow are
meaningless, or whose source shape almost certainly would not have existed in
the original program. A coincidental exact result must be reverted or replaced
with the best honest fuzzy reconstruction and parked with its evidence.
The rule set intentionally omits several noisy shortcuts. It does not condemn
ordinary `volatile` declarations, generic representation casts, `!!value`, or
`value != value`; those can express hardware synchronization, serialization,
boolean normalization, or a NaN test. It also performs no data-flow proof, so a
clean scan cannot establish that variables are initialized or that logic is
sound.
@@ -1,63 +0,0 @@
# source/linker_common: what a comparison base can and cannot earn
*2026-09-22, Lane C. Built at the owner's direction, from January's verified
COMMON records only, and scored separately from the board.*
## What the unit is
`source/linker_common` is not a translation unit. It is csplit's name for the
pooled COMMON block of January's image: 242 records totalling 1,272,664 bytes,
gathered by the linker from tentative definitions across the whole program
(`_render` 643,732, `_ai_debug` 547,628, `_dsound_globals` 30,924, and ~200
smaller ones, down to single-byte debug toggles). `objdiff.json` gives it a
`target_path` and **no `base_path`**, because our build produces no such object -
our COMMON symbols live in their owning objects' symbol tables with section 0.
So it reports 0.000% structurally, not because anything is wrong.
## The base, and its score
`tools/campaign/linker_common_base.py` builds a comparison base containing ONLY
verified records: a symbol January pools that our tree independently emits as a
COMMON symbol of the identical size. Nothing is invented, and nothing
initialised is copied - a `.bss` record carries no bytes, only a name and a
size, and the size used is the one our own objects request. Records we do not
emit, emit at a different size, or emit in a real section are dropped.
kept 58 records 1,214,903 bytes
dropped 184 records 56,787 bytes
Scored on its own (`objdiff-cli report generate` over a one-unit config):
.bss 1,272,568 95.541%
.rdata 96 0.000%
credited 0
**It earns nothing.** Data is credited per section only at 100%, so 95.541% is
worth exactly as much as 0%. The number's value is diagnostic: it says 95.5% of
the pooled block is already correct in our tree, and names precisely what is not.
## What the remaining 4.5% is
181 records, 56,187 bytes, absent from our tree entirely. Only 27 of them
(1,125 bytes) are CRT/XDK/dsound runtime symbols. The rest are genuine Halo
globals no TU of ours declares: `_dsound_globals` 30,924, `_rasterizer_lights`
7,172, `_sound_channels` 6,144, `_wind_globals` 3,340, `_error_globals` 2,056,
`_global_window_parameters` 600, and so on. Plus two pooled `.rdata` records
(96 bytes) that are not COMMON at all and so are outside this base by
construction, and one size mismatch, `_file_location_volume_names`, which is the
out-of-bounds array bug recorded separately.
Closing the section therefore needs those 181 globals declared as tentative
definitions in the right units - spread across many TUs, several of which are
not reconstructed - and it pays nothing until every one of them is present.
## Rules this establishes
* A symbol in `linker_common` was a TENTATIVE DEFINITION in January: declared
with no initialiser. **Never add `= {0}` to such a global** - that moves it out
of the pool. Check `linker_common` before touching any global's initialiser.
This is how the `actors.c` `= NULL` defect was found and fixed.
* Conversely, a global January keeps in its OWN object's `.bss` (director,
render_objects) was initialised in source, and must keep its initialiser.
* Do not wire this base into `objdiff.json`. It would fold a structurally
uncreditable section into the board number without closing anything.
-178
View File
@@ -1,178 +0,0 @@
# Exact-matching methodology
This document records recurring January 2002/XDK 3911 matching failures and
the source-level changes that have actually moved them. It is an operational
checklist, not permission to accept approximate code. Exact status still
requires padded bytes, normalized bytes, relocation count/type/address/target/
addend, ABI, build, and regression evidence.
## Triage decision tree
Before guessing literals or reconstructing diagnostics, consult the
[common constants reference](common_constants.md) and
[shared assertion API](assertions.md). They identify existing owners and
target-specific representation pitfalls; they do not authorize numeric or
source-shape substitutions just because a value looks familiar.
1. Establish the untouched baseline: padded size, meaningful size, normalized
hash, relocation table, first differing instruction, and sibling status.
2. If relocation targets or addresses differ, recover source structure before
tuning registers: statement order, branch fall-through, linkage, field
ownership, and eager versus lazy evaluation.
3. If the target uses 16-bit arithmetic, map every promotion and truncation
boundary. Treat this separately from register allocation.
4. If instruction structure is identical modulo registers, locate a
hardware-forced anchor before changing declarations:
- `rep movs`/`rep stos` force ESI, EDI, and ECX;
- variable shifts force CL/ECX;
- multiply and divide force EAX/EDX;
- ABI/API boundaries may force ECX/EDX or preserve callee-saved registers.
5. For private same-translation-unit helpers, make callers exact first. Start
from the hardware-forced caller and propagate the convention toward the
helper; do not tune the helper in isolation.
6. Stop after five evidence-based source shapes if only an interchangeable
register or dependency-free instruction ordering remains. Record and park
the case instead of perturbing unrelated exact code.
7. Run the strict semantic ledger before assigning work. A function already
listed in `build/semantic_report.json` must not be reimplemented merely
because objdiff displays a fuzzy percentage.
8. When a target macro or expression is known from an exact donor, preserve
its **lvalue topology** as well as its arithmetic. VC7 can emit different
code for mathematically equivalent forms such as transforming a temporary
and transforming the destination member in place. Prefer the donor's
assignment destination, member access, and read-back pattern before
permuting commutative terms or adding artificial temporaries.
## Source-credibility gate
An exact object is necessary evidence, but exact bytes alone do not make a
reconstruction admissible. Retained source must express coherent program logic
and remain plausible for the original January 2002 codebase. Reject inert,
contradictory, undefined, or artificial source whose only credible purpose is
to steer VC7 into the target byte sequence. This includes unexplained empty
branches, self-assignments, fixed conditions, volatile or aliasing tricks,
synthetic lifetime anchors, raw instruction emission, and compiler-control
directives. A target-proven original bug is the narrow exception and must meet
the original-bug policy below.
Review semantics across all reachable inputs, not only the path that resembles
the disassembly. Corroborate unusual topology with January code/data/relocation
evidence, authenticated names or type records, first-party source, or a
credible contemporary analogue such as Marathon. When ordinary, meaningful C
does not reach exactness after the bounded methodology, retain the best honest
fuzzy reconstruction, record the residual, and park it.
`python tools/fake_match_scan.py` provides conservative lexical review leads
for several common smells. Its findings are neither proof of fake source nor a
substitute for manual semantic and provenance review, and a clean scan is not
proof of credibility.
## Blocker classes
### A. Recoverable source-shape mismatch
The emitted differences indicate that the reconstructed C is not yet shaped
like the original source. Productive levers include statement order, branch
polarity, local lifetime, explicit versus inlined expressions, linkage, and
structure ownership.
### B. Integer promotion/truncation mismatch
The target contains operand-size prefixes, partial-register operations, or
`movsx`/`movzx` boundaries that ordinary C promotions erase. Recover the width
of the accumulator and place signed/unsigned casts only at disassembly-proven
boundaries. Do not file this as a register mirror.
### C. Private-ABI/register fixed point
The instruction graph, sizes, and relocations agree, but a translation-unit
cluster assigns interchangeable registers differently. Find a forced-register
caller and solve outward. If the complete cluster remains a pure permutation
after the checklist, park it as a compiler allocation tie.
### D. Scheduler/lifetime tie
Two dependency-free instructions are transposed, or local lifetimes alter
spills without changing semantics. Try only source changes justified by the
surrounding target ordering. Do not add undefined behavior, inline assembly,
compiler pragmas, or flag changes to force the result.
### E. Comparator/reporting mismatch
The machine code is exact but COFF producers encode internal labels or
relocation placeholders differently. Credit only through the hardened
semantic comparator and the reviewed `config/semantic_matches.json` allowlist;
each entry is reverified against current objects during `ninja progress`.
### F. Credible but unclassified residual
The retained source can be semantically and historically grounded even when
the fail-closed classifier cannot distinguish a remaining source-width,
optimizer-context, register, or scheduling cause. Park such a case as
`unclassified` instead of inventing a specific mechanism. An unclassified
park carries no code-generation recipe and grants no exact credit; its ledger
must state the positive source evidence, the unresolved machine differences,
and the evidence required to reopen it.
## Case catalog
| Case | Class | Evidence and source lever | Outcome / next action |
|---|---|---|---|
| `lra_new` (`f0e5a48`) | A | Reordering field initialization to the target's emitted store order aligned the failure block and lifecycle setup. | Exact; make store-order recovery a standard first pass. |
| `object_new` (`claude/object-new-16`, then the isolated XDK scheduler-control lane) | A then D | Writing the cleanup as failure-first eliminated 10 relocation differences and two divergence regions. Only `fmuls` versus deferred `add esp, 0x18` ordering remained. A production-context reproducer then tested 20 one-factor legal-C controls; all forms that moved `fmuls` first also changed x87 dataflow or other bytes. | Preserve failure-first shape; park the dependency-free scheduler tie unless new source provenance identifies a distinct original expression. |
| `hashtable_remove` (`f2d30b9`) | A | Source-oriented comparison order, reload points, and the shared deletion path aligned 496 padded bytes and 20 relocations. | Exact; use comparison polarity and reload timing before register tuning. |
| `csstrcasecmp` (`e5852f8`, `claude/csstrcasecmp-15`) | A then C | `if (c1 != c2)` reproduces the target operand order; `if (c2 != c1)` does not. The remainder is an ESI/EDI parameter/local permutation. | Land only evidence-backed logic changes; handle the remaining mirror through a forced-register caller or park. |
| terminal tab stops (`a917110`) | A | Identical six-byte data still failed because `static` produced storage class 3 while the target symbol was external, class 2. | Exact after external linkage/name recovery; audit symbol class, not just bytes. |
| `generate_private_key` (`public_key_crypt.obj`) | A | Applying the exact `SWAP4` donor arithmetic to a local temporary selected the wrong accumulator form across hundreds of equivalent expression trees. Assigning the helper result to `private_key->dwords[i]` and applying `SWAP4` directly to that same member preserved frontend lvalue provenance and reproduced January exactly. | Exact without assembly, volatility, undefined behavior, or flag changes; preserve donor lvalue topology before classifying a commutative residual as a register tie. |
| `objects.obj` BSS ownership | A/E | Explicit zero initialization plus declaration order recovered the linked 13-byte layout (`object_name_list@0`, `object_memory_pool@4`, `object_globals@8`, debug byte `@12`) without changing any exact function. January PDB/public-symbol and cross-TU-reference evidence proves the first three globals are source-private; csplit had defaulted their manually split labels to external because `symbols.json` omitted `"static": true`. | Keep the globals source-static and mark exactly those three csplit symbols static. Regenerate the split target and require exact size, offsets, storage classes, and whole-TU regression; do not paper over the difference with a semantic allowlist. |
| `tif_packbits.obj` (`7c9c092`) | A | Target linkage, RCSID ownership, `_csmemcpy`, and disassembly-proven TIFF field offsets were required. Repository header layout differed from the target object. | Five functions and data exact; raw offsets are acceptable only with explicit target-layout evidence. |
| random-math globals (`aef5a7c`) | A | Replacing an anonymous address placeholder with the real externally linked 20-byte globals structure fixed BSS ownership and relocation addends. | Exact functions retained; model globals explicitly before function tuning. |
| `default_hash_function` (`f2d30b9`) | B | Target uses 16-bit product/partial-register code while natural C promotes the polynomial and key byte to 32 bits (80 target versus 64 candidate bytes). | Reopen only with a promotion/truncation lattice derived instruction by instruction. |
| LRA verify/free/lock cluster (`f0e5a48`) | C | Seven functions share cache-in-EBX versus cache-in-EDI and private helper conventions. Isolated helper shaping did not move the joint allocation. | Anchor on callers with forced registers, then propagate; otherwise park the whole permutation together. |
| hashtable probe helper `code_0010b270` (`073e794`) | C | Size, relocations, control flow, and operations agree; two return blocks mirror ECX and EDX. | Try return-materialization/single-exit shape once under the checklist, then park if unchanged. |
| `reference_list_copy` lane | C | Target and candidate reduce to the same operations with a callee-saved register mirror after source-shape attempts. | Revisit only from its caller cluster and a hardware-forced anchor. |
| `object_get_marker_by_name` lane | A | Recovering the direct object-header block access removed the artificial wrapper lifetime and reproduced the original call/dataflow structure. | Exact: 272 padded bytes, 11 relocations, and normalized SHA-256 `302db0f36677f304c6d076e0f16f3bb4e9603519b34d650a4311c296bd21c8cf`. |
| `object_compute_node_matrices` (`claude/object-compute-node-matrices` plus the E2 closeout) | D | The corrected C now has the exact 6,560-byte padded size, `0xA44` frame, and 318 relocation identities. The first address drift is relocation 245: January homes `tag_get_name`'s EAX result to `[ebp-8]`, while VC7 keeps EAX live. Independent source, PDB, tail-diff, and legal-C shape audits found no controllable semantic or structural difference. | Park as an EAX-liveness/register-allocation tie. Reopen only if new local-variable provenance or a demonstrated legal-C compiler control appears. |
| `data_decode_array` (`d0a735d`) | E | Padded bytes and 31 semantic relocations are exact; objdiff reports 85.19% because one target symbol plus addends corresponds to several rebuilt local labels. | Credited through the dynamic semantic ledger; no source work required. |
| 37-function switch/local-label set (`24c998a`) | E | Full dual sweep found 37 real functions and 21,051 meaningful bytes exact under conservative COFF normalization but fuzzy under objdiff. | The allowlist is reverified on every progress run; uncertain or changed entries fail closed. |
## Complexity gate
For large aggregate objects such as `objects.obj`, queue a remaining function
only when all of the following hold:
- padded size is below approximately `0x60`;
- local state is small and unambiguous;
- relocation count is low;
- no large inlined helper or private-ABI cluster dominates the function.
Larger functions require a specific leverage reason (new structure proof,
forced-register anchor, or reusable subsystem insight) before consuming an
implementation lane.
## External type-information gate
For the supplied `h1_tags` executables, start with the
[cross-build intake and missing-PDB identities](crossbuild_h1_tags_intake_20260906.md).
The `_symbols.exe` images contain external PDB references, not complete
embedded debug records. That ledger also records the IDA/Ghidra requirement
and separates analysis-generated names from original-symbol evidence.
HCEX, Halo PC, October 2001, and community sources are hypothesis generators.
Every imported field or enum value must carry a January-Xbox disassembly proof.
Unproven spans remain named `reserved`/`unknown` and documented as such. No CEA
offset enters a trusted header merely because it compiles or looks plausible.
## Original-bug policy
Exact builds preserve confirmed original behavior. Mark only defects supported
by binary and layout evidence:
```c
/* BUG (preserved for exact matching): <target behavior and evidence>.
* A corrected build should <safe replacement>.
*/
```
Keep the safe fix disabled in the exact build. Do not label suspicious code as
a bug without target-machine and structure-layout proof.
@@ -1,113 +0,0 @@
# objdiff v3.3.1 mis-scores MSVC data relocations
*2026-09-22, Lane C. Established independently three ways: by reading objdiff's
source at tag v3.3.1, by black-box perturbation of January's own object files,
and by auditing this project's own adjudication ledger. All three agree.*
## The defect
A data section's objdiff score is decided by its **relocations**. Two
relocations are compared by `reloc_eq` in `objdiff-core/src/diff/data.rs`, which
first tries `symbol_name_matches()`. That helper assumes a `$` in a symbol name
is the Metrowerks `symbol$1234` unique-ID separator:
```rust
if let Some((prefix, suffix)) = left_name.split_once('$') {
if !suffix.chars().all(char::is_numeric) { return false; }
...
}
```
Every MSVC mangled string literal contains `?$AA` — the encoded NUL. For example
`??_C@_0P@LFOALBOL@objects_update?$AA@`. The suffix after the first `$` is
`AA@`, which is not numeric, so **the helper returns false for two
character-for-character identical names**.
`reloc_eq` then falls through to `address_eq`, which requires the target literal
to sit at the same offset inside the *synthetic combined* `.rdata` that
`report generate` builds (it forces `combine_data_sections: true`, and
`do_combine_sections` concatenates every same-named section in COFF
section-table order, padding each to `max(align, 4)`). csplit lists January's
`.rdata` COMDATs in image order; CL emits ours in its own order. So the address
test fails too, and a byte-perfect relocation is scored as a mismatch.
That costs the section twice. Relocated bytes are counted twice in
`diff_data_symbol`
match_ratio = (bytes_ratio*len + relocs_ratio*reloc_bytes) / (len + reloc_bytes)
and, worse, the whole-section byte fallback in `diff_data_section` is gated
behind `if all_left_relocs_match`, so it never runs. Since `report.rs` credits
data all-or-nothing per section, one mis-compared relocation forfeits the
entire section.
The code path is immune: `diff/code.rs::reloc_eq` compares names with plain `==`.
Fixed upstream in **objdiff v3.6.0** (2026-01-25), which added
`get_normalized_symbol_name` in `obj/read.rs`; it returns `None` for
`??_C@...?$AA@` so `symbol_name_matches` falls back to exact equality.
## Reproduction
Copy January's `objects.obj`, swap only the two `.rdata` COMDAT section headers
for `_object_memory_release_procs` (24 B) and the `objects_update` literal
(15 B), fixing symbol section numbers; change nothing else. January against
itself scores `.data` 100.0; January against that header-swapped copy scores
`.data` 99.739235 — the exact production number. Swapping two COFF *symbol
table* entries instead leaves it at 100.0, so symbol-table order is inert.
## Scale, measured at HEAD
40 data sections / **32,262 bytes** have identical size, identical raw bytes and
an identical relocation multiset, and would score exactly 100.000000 under an
exact-name rule. Of those:
* **37 sections / 29,582 bytes are already credited** by
`config/semantic_data_matches.json`. No progress bytes are being lost there —
but they are credited as *adjudications*, and the standing rule is that
adjudicated does not count as closed.
* **3 sections / 2,680 bytes are genuinely uncredited**:
`source/ai/actions .rdata` (2,388), `source/ai/ai_communication .data` (276),
`source/ai/actions .data` (16).
## Recommendation
Upgrading the differ to >= v3.6.0 is the root-cause fix. It would recover the
2,680 uncredited bytes and let roughly 37 adjudications be retired, converting
29,582 bytes from adjudicated to genuinely exact. That is a shared build-tool
change and needs an owner decision, and it must be measured board-wide first,
because a differ upgrade re-scores everything.
Do **not** chase these sections with source edits. The only source-side lever is
which `.rdata` COMDATs a TU emits and in what order, so that a literal lands at
January's combined offset — and the target order is csplit's, i.e. the linked
image's layout, which is a link-time property. Reordering or inventing literals
to shift an offset fails the strip test.
Do **not** add new adjudications for them either; that papers over the
instrument. Two entries were retired on 2026-09-22 for the opposite reason —
`cheats .bss` and `tif_version .data` became genuine matches, so their
adjudications were deleted at no cost to credited progress.
## Triage rule this establishes
Run the January-against-January self-diff **before** spending any wave on data,
and **regenerate it whenever the differ changes**. Rewrite `objdiff.json` with
`base_path = target_path` and absolute paths, drop `custom_make`/`build_target`,
then `objdiff-cli report generate`; it takes 0.12 s over all 833 units.
Under v3.3.1 it capped 47 sections / 87,889 bytes. **Under v3.6.0 it caps
nothing at all** - 0 sections, 0 bytes. Every one of those "permanent" ceilings
was this same defect: a January-side string COMDAT comparing unequal to itself,
so even a byte-identical copy could not reach 100. The campaign treated 87,889
bytes as unreachable on the strength of a number produced by the broken
comparator. Do not quote the old figure, and do not kill a target with a stale
ceiling report.
## Resolution
Landed 2026-09-22: `sln.objdiff_tag` pinned to v3.6.0, and the 40 adjudications
the fix makes redundant retired in the same commit. Board effect, measured on
identical objects: raw data +34,886, raw code and functions unchanged, zero
units losing anything, credited data +5,296, adjudications 43 -> 3. v3.8.1 was
measured and rejected - same data gain, but it loses 6 bytes of code.
-91
View File
@@ -1,91 +0,0 @@
# Object exact-match strategy logs
For the completed/interrupted Opus 2 and new Fable 5 lanes, start with the
[current reconciliation queue](../reconciliation_queue_20260907.md). It separates
published progress, donor claims, dirty work, owner holds and tooling defects.
Every object that receives implementation or matching work must maintain one
Markdown ledger in this directory. The filename is the object name with
`_obj.md`, for example `players_obj.md`.
General techniques that apply across translation units belong in
`docs/exact_match_acceleration_playbook.md`; object-specific measurements stay
in the individual ledger.
Start constant and diagnostic recovery with the
[common constants reference](../common_constants.md) and
[shared assertion API](../assertions.md), before testing guessed literals.
The purpose is to make matching work cumulative. A new lane must be able to
distinguish an untried evidence-backed source shape from a spelling that has
already compiled to the same bytes.
## Required contents
Each ledger must include:
1. **Scope and provenance**
- source translation unit and target object;
- exact compiler identity and flags;
- target-build provenance used for names, layouts, or source topology.
2. **Validated baseline**
- exact/total function count and padded code bytes;
- exact/total data sections and bytes;
- for every residual: target/base size, relocation count, normalized hash,
and first meaningful divergence.
3. **Accepted controls**
- the source shapes retained in production;
- why each shape is supported by binary or provenance evidence.
4. **Experiment matrix**
- every meaningful source shape tested;
- resulting size, relocations, normalized hash, first divergence, and
sibling-regression result;
- whether it was retained or reverted.
5. **Do-not-repeat list**
- equivalent spellings or compiler-control attempts already exhausted;
- unsafe or prohibited techniques.
6. **Residual classification**
- source/topology, ABI/register, scheduling, ownership/relocation, data, or
unresolved;
- clearly separate measured fact from inference.
7. **Reopen criteria**
- the exact new evidence that would justify spending another lane.
8. **Disposition**
- strict exact and eligible for `Matching`, still active, or rigorously
parked;
- never grant credit from a fuzzy percentage, equal size alone, or semantic
plausibility.
## Lane rules
- Update the ledger after every bounded experiment batch, not only at handoff.
- Rebuild with XDK 3911 and the repository flags after each retained change.
- Gate every function and owned data section with the hardened comparator.
- Run the whole-translation-unit regression sweep before committing.
- Revert failed source shapes immediately.
- Treat exact bytes as necessary but not sufficient: reject semantically
nonsensical, historically implausible, logically meaningless, or inert
compiler-steering source, even when it happens to match. Coincidental byte
equality never authenticates the source. Park the best honest fuzzy
reconstruction when a natural exact spelling is not available.
- Do not use inline assembly outside a sparse, provenance-backed helper/math
exception. Never use volatile byte forcing, undefined behavior, object-byte
patches, or compiler-flag changes.
- A parked residual must preserve enough evidence for another agent to
reproduce the classification without redoing the full search.
- Label a defect `BUG (original)` only when January binary/source evidence
proves the defective behavior belongs to the original game. A mistake or
omission in our reconstruction is not an original bug. Preserve proven
original behavior in the matching build and document the corrected-build
alternative in a comment without changing the matching token/line shape.
- When retroactively documenting an old lane, state which experiment details
were not preserved. Never manufacture source variants, hashes, or outcomes
to make a historical matrix look complete.
## Minimal experiment row
```markdown
| ID | Source shape | Size T/B | Relocs T/B | Base hash | First divergence | Siblings | Decision |
|---|---|---:|---:|---|---|---|---|
| E01 | ... | `0x.../0x...` | `N/N` | `...` | `+0x...: ...` | `K/K exact` | reverted |
```
@@ -1,113 +0,0 @@
# action_alert.obj complete reconstruction and fuzzy closeout (2026-09-01)
## Result
`source/ai/action_alert.obj` advanced from five exact functions and seven
unwritten functions to eleven exact functions and one complete, documented
fuzzy residual. There are no unwritten functions left in the object.
The newly exact functions are:
- `action_alert_setup`
- `action_alert_flush_position_indices`
- `action_alert_flush_structure_indices`
- `distance_squared3d`
- `action_alert_update`
- `action_alert_perform`
The remaining private function was recovered as
`action_alert_next_position`; the address-derived symbol was removed from
`config/symbols.json` and the symbol remains translation-unit private.
## Evidence and donor audit
The reconstruction was checked against all Action Alert ledgers, repository
history, registered branches and worktrees, the January target disassembly and
relocations, and the later HCEA semantic sources under:
`research-cache/halocea-full-blobs-20260830/src/blam/ai/action_alert_*.c`
Pastudan's setup/update reconstruction was also inspected. Marathon contains
no direct Action Alert donor. No registered worktree contains a complete
definition of either `action_alert_perform` or `action_alert_next_position`,
and no farther candidate than the retained source was found.
The HCEA source and AI definitions corroborate the complete behavior:
- disabled/passenger, no-order, and no-encounter rejection;
- encounter, squad, and move-position traversal;
- current-position, actor-distance, noncombat-sequence, and nearby-prop
availability tests;
- cseries bit-vector tracking of unavailable positions;
- weighted random selection and forward, ping-pong, and per-tick-random
traversal;
- dwell-time selection, animation startup, and move-position state updates.
Action Alert prototypes now live in the closest owner header, `actions.h`,
rather than as ad hoc declarations in `actions.c`. Accesses use the project's
typed actor, unit, definition, tag-block, and scenario helpers. The source
uses semantic cseries types and enum constants and keeps explicit returns.
## Residual measurement
Focused gate after reconstruction:
```text
residual 784 _action_alert_next_position [size 768!=784, sha]
== exact 11 residual 1 unwritten 0 (of 12 listed)
```
Fresh full campaign-report similarity for the residual is `99.46479%`.
This value, rather than a separate aligned-disassembly score, is recorded in
`config/parked.json`.
- Target: 784 padded bytes, 18 relocations, normalized SHA-256
`677fd6cc1a9e66de32123c4614de5a0cc1188501baee69ea20fd5bf74eac3fe4`.
- Candidate: 768 padded bytes, 18 relocations, normalized SHA-256
`4e16c85b544ded3352df48ee7baa0e19b5a34a45e421a975cf11cd771184f371`.
Aligned instruction comparison is retained only as diagnostic evidence. It
shows that the geometry, prop-iterator, bit-vector, random-selection, and
ordered-traversal body is byte-identical. The remaining decoded differences
are limited to January preloading the `NONE` result in EAX, the resulting
AL/CL and EAX/ECX allocation choices in the entry guard, two equivalent
full-EAX versus AX `NONE` returns, and target-envelope padding.
## Bounded source-shape audit
Ordinary-C probes covered combined, separate, and enclosing entry guards;
short and long default-result lifetimes; declaration order; direct and
assigned random returns; natural `for` and `while` traversal; and switch
default/case arrangements. The retained `while` traversal reproduces
January's initialization schedule, and initializing the traversal direction
before a semantic enum switch with no redundant default reproduces January's
entire switch body. The remaining entry/result variants either returned to
the same compiler fixed point or disturbed larger exact regions.
No volatile or register forcing, compiler barrier, pragma, inline assembly,
fake dependency, raw structure offset, undefined behavior, forced inlining,
or nonsensical control flow is retained. The function is parked as
`register-allocation` and should reopen only for authoritative January source
or local-variable records, or a natural compatible-compiler donor that
explains the default-result lifetime.
## Validation
The admission run passed:
- full `ninja`, including report, semantic report, and progress validation;
- an independent strict per-function sweep against a clean build of base
`f73b19d30`: 5,026 exact to 5,032 exact, exactly the six Action Alert gains
above, with zero lost functions and zero unit-read errors;
- focused gates: Action Alert `11/12` exact with one parked residual, Actions
`58/63` exact with its five pre-existing parks, Units `189/189`, objects
`121/121`, and object types `31/31`;
- `python -m tools.parked_functions`: 78 active, zero stale, zero invalid;
- `python -m pytest -q`: 255 passed;
- semantic audit: 5,032 strict exact functions, 134 hidden exact
functions, and zero unit errors;
- fake-match scan of all changed C/header files: zero findings;
- object-admission audit: no contradictions or revocations (one unrelated
pre-existing `player_rumble` ownership-review candidate);
- all-unit `point_from_line3d` owner comparison against the base: no new or
lost COMDAT owner.
@@ -1,152 +0,0 @@
# `action_alert.obj`: `distance_squared3d` wrapper fixed point (2026-08-26)
## Outcome
No production source change and no matching credit. One evidence-backed
candidate disproved the locally renamed inline-wrapper topology for
`_distance_squared3d`: the candidate emitted 48 padded bytes, while January
and every exact donor emit 64. The source was immediately reverted without a
tuning compile, the committed five-function baseline was rebuilt, and its
whole-TU manifest is green.
`action_alert.obj` therefore remains 5/12 exact functions and 177/2,120
meaningful code bytes. The retained production commits remain:
- `020449c3b3518fd6bbc1dfbe739ab3a15762befa`
(`Match action alert geometry wrappers`);
- `5196b8e4b409c571abc6be5634d0d09bc90ac9e5`
(`Record action alert geometry validation`).
## Frozen target and baseline
The clean committed translation unit was frozen at `5196b8e4` in:
- `build/audit/action_alert_distance_squared_baseline_20260826.json`;
- manifest SHA-256
`f1ceaca1e7bd5c97bbac65192214909d4068cb9532af1f0cd29973938a3ec30c`.
The January target is:
- 49 meaningful / 64 padded bytes;
- zero relocations;
- normalized SHA-256
`f42fa39f289bfa50174aa61e77b345b092df8329d46f43f88ca127227ad1e9c8`;
- split-object SHA-256
`bb7a70aae881cdf5672ae48c3b9c5b7d0cdcc9a30a90730672f04b71fa1cab5f`.
The baseline source SHA-256 is
`0682c4b9fbf896d8434a1a8f6f78b355c2fabf20669601be3d25f64541fee93b`
and its Git blob is `8de8debb9ac7108acc7352c53e36e305f75d06f7`.
## Same-name donor census
The canonical nested body is owned by `source/math/real_math.h`:
```c
__inline real distance_squared3d(
real_point3d const *a,
real_point3d const *b)
{
real_vector3d v;
return magnitude_squared3d(vector_from_points3d(a, b, &v));
}
```
The read-only census script
`build/audit/find_action_alert_distance_donors_20260826.py` has SHA-256
`c8989f86e767ca76965766eace71c8f8a9d326d96b32545df14e219fd3a32e72`.
It found twelve rebuilt objects defining the same-name section, and hardened
comparison reports every one `all_equal: true` against the action-alert
target:
- `source/ai/action_fight.obj`;
- `source/ai/action_guard.obj`;
- `source/ai/action_search.obj`;
- `source/ai/action_uncover.obj`;
- `source/ai/actor_perception.obj`;
- `source/effects/material_effects.obj`;
- `source/game/game_engine.obj`;
- `source/objects/objects.obj`;
- `source/physics/breakable_surfaces.obj`;
- `source/physics/collisions.obj`;
- `source/physics/physics.obj`;
- `source/units/units.obj`.
Direct hardened comparisons against both `action_fight.obj` and `units.obj`
independently prove the 64 padded bytes and zero-relocation inventory. The
donors obtain the COMDAT from a real call to the canonical header inline;
none supplies an ordinary out-of-line source definition that can be
transplanted verbatim.
## Sole candidate and rejection
The candidate locally renamed `distance_squared3d` across the header include
and added the ordinary wrapper:
```c
real distance_squared3d(
real_point3d const *a,
real_point3d const *b)
{
return distance_squared3d_inline(a, b);
}
```
This is ordinary C89 and obeys all formatting and return rules. Exactly one
normal VC7 compile was spent. The preserved first candidate is:
- `build/audit/action_alert_distance_squared_first_current_tu_20260826.obj`;
- raw SHA-256
`c20dfa1ae881e1fae04d85b0a1f8e8911c837ece78b17bc33f64bcac6d5734c6`.
Its `_distance_squared3d` section is:
- 48 padded bytes;
- zero relocations;
- normalized SHA-256
`3e3811d46757035eda49d4b9374d60a716646e589c018726d37a8e0ae2e3564a`.
The wrapper therefore changes the compilation context enough for VC7 to
collapse the canonical nested operation into a shorter form. It is not the
same emission topology as the donor COMDAT. The candidate whole-TU gate also
fails closed on the additive symbol/debug inventory and records
`_distance_squared3d` in `changed_nonexact`. Direct comparison proves the five
accepted bodies themselves remained byte-exact, but the manifest failure is
independently sufficient to reject the candidate.
No second spelling, hand expansion, compiler option, object permutation,
allowlist, adjudication, exception, synthetic caller, or byte-forcing form was
compiled.
## Restoration
The candidate source was removed with `apply_patch`, returning
`source/ai/action_alert.c` byte-for-byte to the committed blob. The generated
object path was resolved inside the authoritative worktree, that single object
was deleted, and one restoration build was run. Its nondeterministic raw
SHA-256 is
`28105c5cf2ead523cf5e98dfb87afb09330164d4e5cb233f3c776167345f92b3`.
The frozen manifest then reports:
- `ok: true`;
- no failures or warnings;
- `changed_nonexact: []`;
- `_action_alert_begin`, `_action_alert_control`,
`_vector_from_points3d`, `_magnitude_squared3d`, and
`_real_random_range` all `still_exact`.
Direct hardened comparison of the same five functions remains
`all_equal: true`. No protected path changed, no tracked path was deleted,
and nothing was pushed.
## Reopen criterion
Do not retry the renamed-inline wrapper. Reopen `_distance_squared3d` only
with genuinely new evidence that reproduces the donor COMDAT emission context
without a synthetic caller—for example an authenticated ordinary definition,
a grounded VC7 inline/COMDAT topology explanation, or another natural
translation-unit use that makes the canonical header definition emit under
the function's real ownership. A direct ordinary copy of the canonical body
is a distinct unmeasured hypothesis, but it is not justified for another
production compile merely by the failed wrapper result.
@@ -1,124 +0,0 @@
# `action_alert.obj` Jonas prefix recovery
This Jonas/Codex ledger records a bounded, fail-closed recovery of the five
requested prefix bodies in `source/ai/action_alert.obj`. Only
`_action_alert_control` was independently strict on the first natural
candidate, so it alone is retained. The object advances from 2/12 to 3/12
exact functions and remains `NonMatching`; no data or whole-object credit is
claimed.
## Scope and provenance
- Authoritative integration base:
`7d2ff479dde012f94cc08857e4cc74650b85b9af`.
- Baseline `action_alert.c` blob:
`45f0eae365e5f6e087507143bd68549c676ab736`.
- Retained `action_alert.c` blob:
`01c9af4f179f4fc7e3792413c7a18cf3d7899c5a`.
- Retained source SHA-256:
`d10627b7393f20bd86037df3da279333b2c0e99c0a7adb24d77320e5865d5865`.
- January executable SHA-256:
`4cc87b45f721270392a96f1674ed2b5cd4a7bb4355faeab4531d1cf1884d9520`.
- January split-object SHA-256:
`bb7a70aae881cdf5672ae48c3b9c5b7d0cdcc9a30a90730672f04b71fa1cab5f`.
- The raw candidate-object hash is deliberately omitted because the XDK COFF
container timestamp changes across forced builds. The normalized function
hashes below are the reproducible admission identities.
- Clean HCEA semantic donor commit:
`c168af2e747d3095d9a29418ae401f3a39544863`. The screened source blobs
are `a8494315eb9fbbc0b57fe9ae169b76f5203044fe` (setup),
`bdc904682c5a2ba16a90f308cfeef79c97be2a68` (control),
`16f1703d50e1daa0b8f9fa15a6d5aad76c3a484d` (flush position),
`3018bec7791130ca7a11f95f320d0c05cf257f81` (flush structure), and
`c4f463bf4613fc0240debabaa8ad40cc494eb252` (update).
HCEA supplies semantic names and natural control-flow evidence. The January
COFF remains the sole authority for bytes, padding, relocations, and symbolic
destinations.
## One-shot strict result
The five requested bodies were compiled together once with the repository's
natural XDK 3911 command. Each body was compared independently with the
hardened COFF comparator. The four nonexact bodies were then removed without
another source-shape experiment or any code-generation tuning. The later full
build and forced regression are validation replays of this fail-closed final
source.
| Function | Target meaningful | Target padded | Relocations | Target normalized SHA-256 | First candidate result |
| --- | ---: | ---: | ---: | --- | --- |
| `_action_alert_setup` | 126 | 128 | 7 | `225015f380c74c1a19d563730b7820375cd37490eb40cb6d67e3404bc46c9cc0` | rejected: 144 padded, 7 relocations, SHA `47b3dc2358c61240e9d48276b9d00caf88d8384cfc505e764f2929cf32ba7491` |
| `_action_alert_control` | 73 | 80 | 3 | `8be5d8f243b76cbf9dd2c4f7f35b4509c349bc970a9b2fc1518a71e3e6a1fbb3` | **retained: strict exact** |
| `_action_alert_flush_position_indices` | 40 | 48 | 2 | `02db3d8b0b933d6e26b970c3c7a3639e7005e78304692915295d835f61eff579` | rejected: 48 padded, 2 relocations, SHA `90abe46d68627d1a83f1a5a89d4d7ecd6d86b0ee14b514917b19c73f96d17335` |
| `_action_alert_flush_structure_indices` | 39 | 48 | 2 | `1b6fb9035cd64ac3bd1a66e1be208f3dc09ea95d32ef17f960e5bd960540a466` | rejected: 48 padded, 2 relocations, SHA `75fef43de1d32d255e61481ac5402bab1d683f3c801fb2a252438d8ba603434d` |
| `_action_alert_update` | 323 | 336 | 13 | `0b4be7a9c0704dc3075a31fabf85a1f5ab7868be9d5fc3676685252d0aeaaa43` | rejected: 336 padded, 13 relocations, SHA `13ed6e0ef99086fe5eb49463dfa54e086057c8155982114cd78ea8e212608dea` |
The retained control body is exact in padded size, every normalized byte, and
all three relocation addresses, types, destinations, and addends. It uses the
established typed `actor_get` and `actor_definition_get` accessors, sets the
January-proven noncombat idle-look value `1`, tests the named actor-definition
bit 6, and sets both crouch Booleans when that bit is active.
The wave adds 73 meaningful and 80 padded exact code bytes. Together with the
pre-existing exact `_action_alert_begin` and `_real_random_range`, the final
object measures 3/12 exact functions, 101/2,120 meaningful code bytes, and
128/2,240 padded code bytes. The preserved functions were replayed directly:
| Preserved function | Meaningful | Padded | Relocations | Normalized SHA-256 |
| --- | ---: | ---: | ---: | --- |
| `_action_alert_begin` | 1 | 16 | 0 | `499f1f307c1cb989f968a6b7fcaec591e1828877223d0b0e7e8e8b76cde8c9ca` |
| `_real_random_range` | 27 | 32 | 2 | `7ed2e50e8e1a701a98fcceeb6a6b67e05d997f21bdb9ff5ee2e848fd1a8f409a` |
## Fail-closed omissions and policy boundary
The setup, both flushes, and update definitions are absent from the final
candidate. The two large target-only bodies `_code_00000950` and
`_action_alert_perform` were never added or compiled in the candidate lane.
The rejected update's temporary local 0x3C animation-reference view and its
scenario, object, and unit includes were removed with the body. No shared
header, tag-definition storage, configuration, semantic exception, parked
record, or completion label changes.
The retained source is readable typed C with vertically formatted parameters
and an explicit terminal `return;`. It contains no assembly, raw object-header
access, pointer or union pun, inactive-union access, integer-address
reconstruction, volatile scheduling device, force-inline annotation,
optimizer pragma or barrier, synthetic anchor, byte-forcing expression, or
undefined-behavior match.
The target owns 80 ordinary non-code bytes; the final candidate owns none and
receives zero data credit. The existing exact begin and random-range bodies
are textually unchanged.
## Validation
- Complete `halobetacache_build` and `libcmt_build`: pass.
- Direct hardened comparison: retained control plus both preserved functions
pass with exact normalized bytes and relocations.
- Semantic audit: 470 units, 3,995 functions evaluated, 3,855 semantic exact,
101 hidden exact, 36 ordinary-only, 3,916 accepted exact, and zero unit
errors.
- Campaign progress: 375/833 complete objects, 3,903/11,060 exact functions,
472,316/2,198,102 exact code bytes, and 1,835,088/4,176,062 matched data
bytes.
- Halo progress: 273/468 complete objects, 3,736/7,574 exact functions,
459,402/1,770,166 exact code bytes, and 1,829,896/3,923,451 matched data
bytes.
- Object-admission audit: zero candidates and zero revocations; only the
inherited `source/shell/shell_xbox` completion-label contradiction remains.
- Parked-function audit: three active, zero stale, and zero invalid entries.
- Complete tooling suite: 179/179 tests pass.
- Clean committed-state forced single-unit regression snapshot/check: all
three accepted functions remain exact, with no failure or ownership change.
- `git diff --check`, deleted-path, policy-token, and frozen-scope audits:
pass.
The five frozen large-object sources are byte-identical to the integration
base: `vehicles.c` (`4a01dc7c54717e1b063a0cac72b0178b4bb2bf04`),
`ai_debug.c` (`517731845431f1e240b06742ab9ddfbf6e67e929`),
`ai_script.c` (`c3c858af2e4d6bf1df6dccf013b9ba6ee09f332d`),
`actions.c` (`b594c1d52afe683e3fc794be08bf40e2b7045e26`), and
`units.c` (`19a15c4a3a47849ffd8ba0c13989c0c389452519`).
Only `source/ai/action_alert.c` and this new Jonas ledger are tracked changes.
No tracked file is deleted, no object is marked complete, and nothing is
pushed.
@@ -1,212 +0,0 @@
# `action_alert.obj`: safe geometry pair (2026-08-26)
## Outcome
Two ordinary geometry helpers are newly strict-exact:
- `_vector_from_points3d`;
- `_magnitude_squared3d`.
The wave adds two exact functions and 76 meaningful code bytes.
`action_alert.obj` advances from 3/12 to 5/12 exact functions and from 101
to 177 matched meaningful code bytes. It remains incomplete; no completion
label, semantic exception, adjudication, data credit, or comparator waiver was
added.
Source commit:
- `020449c3b3518fd6bbc1dfbe739ab3a15762befa`
(`Match action alert geometry wrappers`).
## Frozen evidence
The untouched translation unit was frozen at commit
`fe2e8631136da957ce6fe286fde456a480cbc2ba` in:
- `build/audit/action_alert_safe_geometry_pair_baseline_20260826.json`;
- manifest SHA-256
`769e1bda19900e12f7c3622d1e4f6632debc1ef84b5fcc6fe42ca72ce10c089d`.
The target functions have these authenticated January fingerprints:
| Function | Meaningful / padded bytes | Relocations | Normalized SHA-256 |
| --- | ---: | ---: | --- |
| `_vector_from_points3d` | 38 / 48 | 0 | `905f56dbe4597d2bacc201456ffc33464b166e0b6392dda24a1a3e109b637aa2` |
| `_magnitude_squared3d` | 38 / 48 | 0 | `0d04579096eeab3a1c45f87774e1eb20ce4856dbac219f624a855e8c9ae99725` |
The canonical bodies and ABIs are fixed by `source/math/real_math.h`.
Direct comparison against the exact same-name definitions in
`build/base/source/math/real_math.obj` proves all 48 padded bytes of each
function. The repository-wide same-name census also found 25 exact donors for
`_vector_from_points3d` and 33 for `_magnitude_squared3d`. Both target
sections have zero relocations, so there is no unresolved callee, owner, or
addend hypothesis. This is a same-name, same-ABI transfer rather than a
masked-shape guess.
`_distance_squared3d` was deliberately excluded. Its target section is 49
meaningful / 64 padded bytes with zero relocations and normalized SHA-256
`f42fa39f289bfa50174aa61e77b345b092df8329d46f43f88ca127227ad1e9c8`,
but its canonical source nests another helper. That makes definition order and
inline topology a separate evidence question; it was not bundled into this
independent two-function wave.
## Claude documentation and tooling audit
All locally reachable Claude records that name this object or the applicable
matching tools were reconciled before candidate selection.
- The Claude residual census at
`C:/halo-worktrees/claude-near-complete-objects-20260816/research/residual_frontier.json`
lists the three geometry functions only as January `target_only` sections.
It records no candidate, residual measurement, source experiment, or fixed
point for the selected pair. The file SHA-256 is
`db87717092d2e7c71160619e9df25f0b940f1762725c81f8bd2d6ece77fbde0d`.
- No reachable `claude/*` branch changes `source/ai/action_alert.c`. The
current history contains the prior Jonas/Codex prefix, control, and random
range recovery only.
- The Claude copies of `docs/matching_methodology.md` and
`docs/shape_transfer_executor_strategy.md` are byte-identical across the
near-complete, finish-hs, and untried worktrees and to the current files.
Their SHA-256 values are respectively
`3eb52f6b2df7b94b4d732161c0f86e2becebbcf0b546824deb957882118b1b5c`
and
`072ee7b2b3a2900e401bec7f69bff475f57639456d9c6270d2ea51189aaefa83`.
- Claude's `docs/house_rules.md`, SHA-256
`9294b81dc2e1c92d75ffd2f9f289287131bdba3564ba95bb054b08189781bfe1`,
requires ordinary C, canonical vector expressions, and evidence before a
compile. The retained component expressions are the canonical header forms
and are corroborated by exact same-name donors.
- Claude's flag-provenance census confirms this TU uses the normal
`/O2 /Oy- /DDEBUG /Dxbox` profile and contradicts `/QIfist` as a whole-TU
alternative. No flag change was considered or used.
- The Claude campaign README identifies
`tools/coff_compare.section_infos_equal` as the acceptance gate. The
current hardened comparator and whole-TU regression manifest were used
directly; no in-memory edit harness, alias adjudication, or comparator
exception was used.
The earlier local record
`docs/object_matching_logs/action_alert_obj_jonas_prefix_recovery.md` was also
read in full. Its one-shot covered setup, control, the two flush helpers, and
update; only control survived. It did not compile or measure either helper in
this wave, so no fixed point was reopened.
## Candidate discipline
The two inline identifiers are locally macro-renamed across the include block,
then ordinary out-of-line definitions are appended after the already accepted
random-range wrapper. `action_alert.c` has an empty private-code tail, so the
append preserves every existing function section index. The macros only avoid
duplicate C definitions; they do not alter optimization, calling convention,
or emitted instructions.
The one normal VC7 candidate compile was strict-exact for both functions on
the first attempt. There was no object permutation, compiler-flag change,
allowlist, adjudication, comparator exception, object patch, intrinsic,
alignment directive, synthetic anchor, or byte-forcing expression.
The preserved first candidate is:
- `build/audit/action_alert_safe_geometry_pair_first_current_tu_20260826.obj`;
- raw SHA-256
`76977841b7e1bac6c02bf3ca86ca56785c5d47a6f3d55bda7019847d270e0ad4`.
## Exact comparison and frozen gate
The hardened comparator reports `all_equal: true` for both additions and all
three previously accepted functions:
- `_vector_from_points3d`;
- `_magnitude_squared3d`;
- `_action_alert_begin`;
- `_action_alert_control`;
- `_real_random_range`.
The frozen gate reports exactly the pair as `newly_exact`, all three inherited
functions as `still_exact`, and `changed_nonexact: []`. Its sole hard finding
is the expected additive symbol inventory from defining the two functions.
## Clean committed state and replay
The clean committed manifest is:
- `build/audit/action_alert_safe_geometry_pair_committed_20260826.json`;
- commit `020449c3b3518fd6bbc1dfbe739ab3a15762befa`;
- SHA-256
`940551915655b88e2affb548bb9e62d687ec109d672b4d02dde0a79cd85af98f`.
Its post-rebuild check returns `ok: true`, no failures, no warnings, no
changed nonexact functions, and all five functions above as `still_exact`.
The generated object path was resolved inside this worktree. That single file
was deleted and rebuilt once from the committed source. Raw base-object hashes
were:
- before replay:
`76977841b7e1bac6c02bf3ca86ca56785c5d47a6f3d55bda7019847d270e0ad4`;
- after replay:
`c7646f7851aa760a7de32c2b52f9b391137eae2ede49cb028cb74740e9810acb`.
VC7 debug/build records are nondeterministic, so raw object hashes are not the
equality oracle. The post-replay manifest is green and direct hardened
comparison remains `all_equal: true`.
Pinned artifacts:
- target split object:
`bb7a70aae881cdf5672ae48c3b9c5b7d0cdcc9a30a90730672f04b71fa1cab5f`;
- source file before/after:
`84e4f2c3591554c6d34dc0701173bbfb11fb137ff4053a571cf3a55041d252ad` /
`0682c4b9fbf896d8434a1a8f6f78b355c2fabf20669601be3d25f64541fee93b`;
- source blob before/after:
`01c9af4f179f4fc7e3792413c7a18cf3d7899c5a` /
`8de8debb9ac7108acc7352c53e36e305f75d06f7`;
- canonical report:
`932d130576c91043f603dec2712ad9afb5dc9bfc98a1d5a991af3ebf482ca4f6`;
- semantic report:
`33c07f434de44fc536e8ba0ddbf98b3d4eb0b9056fe243e84dceed5ec6721bde`;
- VC7 compiler:
`483e00c47bb08d699475a642bcff15b5b2036350b31c540e88a506baf101da11`;
- hardened comparator:
`411e41698a9491a7b69430307d03f844ee3144633e5d3038d3ffde81a8eccfb7`;
- regression gate:
`d09b46dd2331ff7ca677cad78a66c780c4effa41a5389f0c614c9760d7632a5f`.
Supporting audits:
- admission SHA-256:
`f0aa45c22cc604fb74abc388b06dd79a27e7a0b498a5d28837a9e785680c9a36`;
- parked-frontier SHA-256:
`cfac03aea5be607c19a6a40bcdbc1164e71ffb0179602af4bd37b3fd50e26d1d`.
## Canonical progress and gates
After regeneration:
- canonical code: 508,806/2,198,102 bytes and 4,215/11,060 functions;
- complete files: 377/833;
- Halo category: 495,892/1,770,166 code bytes and 4,048/7,574 functions;
- Halo complete files: 275/468;
- semantic audit: 470 units and 4,323 functions evaluated, 4,180 semantic
exact, 114 hidden-exact functions / 64,705 hidden bytes, 4,241 accepted
exact, and zero unit errors.
Repository gates:
- object admission: zero candidates and zero revocations; only the inherited
`shell_xbox` completion-label contradiction remains;
- parked validation: three active, zero stale, zero invalid;
- tooling tests: 179/179 pass;
- Halo and libcmt dry graphs: no work;
- protected-path touches: zero;
- deleted tracked paths: zero;
- no push.
## House-rule audit
Every parameter is on its own line and both functions end with explicit value
returns. The retained source is C89. It contains no assembly, `register`,
`volatile`, barrier, pragma, explicit intrinsic, attribute, raw address,
pointer/integer reconstruction, pointer or union pun, undefined behavior,
synthetic anchor, or byte-forcing form. These functions perform no tag or
object access, so the typed-access rules are not implicated.
@@ -1,119 +0,0 @@
# `action_alert.obj`: setup store-order one-shot rejection (2026-08-30)
## Scope and result
This lane tested one independently sourced ordinary-C shape for
`_action_alert_setup` and no other residual owner. The candidate was rejected
after its first compile. The source was restored immediately; the object
remains **5/12 exact** and **224/2240 padded bytes exact**.
The five inherited exact owners were rechecked after restoration:
- `_action_alert_begin`
- `_action_alert_control`
- `_vector_from_points3d`
- `_magnitude_squared3d`
- `_real_random_range`
## New premise
The fresh premise came from the readable Pastudan history at commit
`1146f5b799111ab72f2f5e37303a4c050945b88b`, file
`src/halo/math/vector_math.c` (the action-alert implementation around lines
368-388 in that snapshot). Unlike the previously tested HCEA-derived shape,
it preserves the January target's post-`csmemset` field-store order:
1. conditional `move_position_order`
2. `target_move_position_index = NONE`
3. `pending_move_position_index = initial_move_position_index`
4. `move_position_increasing = TRUE`
5. `wait_ticks = 0`
6. `must_play_animation = FALSE`
The canonical declaration was retained unchanged:
```c
boolean action_alert_setup(
long actor_index,
short move_position_order,
long initial_move_position_index,
struct alert_state_data *state_data);
```
This was deliberately only a store-order adoption, not the complete Pastudan
lift. The Pastudan snapshot's recovered formal sequence at line 372 is
`int actor_handle, int value, short field, void *state`: its second argument is
full width and its third is narrow. This lane retained the canonical
`long, short, long, pointer` declaration rather than changing a cross-TU ABI
as part of the source-shape test.
The body used typed fields, `actor_get`, the line-35 `state_data` assertion,
`csmemset(state_data, 0, sizeof(*state_data))`, and the store sequence above.
It used no inline assembly, `volatile`, `register`, pragmas, raw structure
offsets, type punning, or byte-forcing constructs.
## Strict comparison
Only `build/base/source/ai/action_alert.obj` was scheduled; the Ninja dry run
showed exactly one `CL` edge. The first and only candidate compile produced:
| Measurement | January target | Candidate |
|---|---:|---:|
| Padded section size | 128 | 128 |
| Relocations | 7 | 7 |
| Normalized SHA-256 | `225015f380c74c1a19d563730b7820375cd37490eb40cb6d67e3404bc46c9cc0` | `daee1bee0eae9da35692ad6ae86ae233ed996083745a65e1f1303842181b29c2` |
All seven relocation offsets and destinations were identical. The first
normalized byte divergence was at section offset `+0x55`: target `0x03`,
candidate `0x04`; 40 normalized bytes differed in total. The local contexts
were:
- target: `0c84c0740433c0eb038b450c668b55106689065f66c74606`
- candidate: `0c84c0740433c0eb040fbf450c668b55106689065f66c74606`
Decoded at `+0x54`, the target has `eb 03 8b 45 0c`: it jumps over a
three-byte `mov eax, dword ptr [ebp+0x0c]`. The candidate has
`eb 04 0f bf 45 0c`: it jumps over a four-byte sign-extending word load. The
first miss therefore reflects the retained `short move_position_order`
formal, not a difference in the Pastudan-derived post-memset store sequence.
This is recorded as provenance evidence, not used as a tuning instruction.
The earlier rejected setup candidate was 144 padded bytes with seven
relocations and normalized SHA-256
`47b3dc2358c61240e9d48276b9d00caf88d8384cfc505e764f2929cf32ba7491`.
The new provenance therefore improved the section extent and relocation
topology, but strict byte identity still failed.
## Restoration and boundary
The setup body was removed immediately, the baseline object was rebuilt only
to restore it, and `tools/coff_compare.py` reconfirmed all five inherited
owners exact. The restored source blob is
`8de8debb9ac7108acc7352c53e36e305f75d06f7`; the January split object SHA-256
is `bb7a70aae881cdf5672ae48c3b9c5b7d0cdcc9a30a90730672f04b71fa1cab5f`.
Do **not** tune this Pastudan store-order candidate from the observed byte
differences and do not retry it. Reopen `_action_alert_setup` only with a new,
independent prototype/source-shape adjudication. A defensible future premise
would need to establish the likely `long, long, short, pointer` prototype and
its cross-translation-unit impact in `source/ai/actions.c`; the byte difference
alone is insufficient. The two flush helpers,
`_action_alert_update`, `_distance_squared3d`, `_action_alert_perform`, and
`_code_00000950` were not compiled in this lane. In particular, current HCEA
flush topology is not a new enough premise to override their existing
fail-closed boundaries.
## Documentation and tooling consulted
- `docs/matching_methodology.md`
- `tools/campaign/README.md`
- `docs/object_matching_logs/README.md`
- the three earlier `action_alert.obj` ledgers
- repository `CLAUDE.md` guidance from the canonical, Stian, and Pastudan trees
- January `build/split/source/ai/action_alert.obj`
- `tools/coff_compare.py` and `tools/campaign/board.py`
- current HCEA action-alert history and the Pastudan snapshot named above
The Discord note that Bungie's fast `ftol` may have been handwritten applies
only as Matrix Math provenance. It did not alter this lane's ordinary-C-only
admission rules.
@@ -1,193 +0,0 @@
# Action Alert: restore genuine math-header ownership (2026-09-05)
## Disposition
Root has applied the single source-only normalization packet to canonical
Action Alert. The actual full canonical Ninja build passes, and the complete
stable sweep remains 6,217 exact functions out of 8,245, with zero losses.
Root's actual all-section comparison and full tooling/tests also pass.
The actual Ninja object is runtime-identical to the reviewed PID candidate:
SHA-256 `c15f370e118de1a78c8cbcab7e3d4cd07f255d2c50af6c4b81e7c2cf391b46da`.
The exact baseline-to-actual changes are only the three selection transitions
and two unreferenced alias removals documented below. All 281 parks validate,
the admission audit has no contradictions or revocations, and 288 tests plus
26 subtests pass. The pytest cache permission warning affects only caching.
Root evidence: `scratch/math-owner-schema-actual-action_alert-20260905.json`,
`scratch/math-owner-schema-actual-summary-20260905.json`, and
`scratch/math-owner-schema-canonical-first-20260905.json`.
The first natural candidate retains all
11 strict exact functions, the identical existing complete residual, all six
runtime data owners, and every gameplay ABI and instruction. No exact bytes
are gained or lost. The object remains 11 exact / 1 residual / 0 unwritten,
not whole-object Matching.
This is an ownership correction, not a new gameplay or residual experiment.
Canonical production source, headers, configuration and build outputs were
not changed by this worker.
## Source provenance and prior adjudication
Read in full before the test:
- `docs/matching_methodology.md` and `tools/campaign/README.md`;
- `action_alert_obj_jonas_prefix_recovery.md`;
- `action_alert_obj_jonas_safe_geometry_pair_20260826.md`;
- `action_alert_obj_jonas_distance_squared_fixed_point_20260826.md`;
- `action_alert_obj_jonas_setup_store_order_rejection_20260830.md`;
- `action_alert_obj_complete_fuzzy_closeout_20260901.md`;
- `action_charge_obj_jonas_math_helper_recovery_rejection_20260826.md`;
- `action_charge_obj_jonas_math_helper_recovery_v2_20260826.md`;
- `action_charge_obj_jonas_real_random_recovery_20260826.md`;
- `game_sound_obj_obstruction_current_owner_boundary_20260905.md`.
The geometry-recovery ledger explicitly introduced local pre-header aliases
and duplicate ordinary external math bodies to add selected code owners while
the real gameplay callers were still unwritten. It described the macros as
duplicate-definition avoidance; they nevertheless changed ordinary COMDAT
ownership and now materialize two additional alias-named helper bodies.
The complete 20260901 reconstruction introduced the genuine gameplay uses.
The old distance fixed point tested a fourth renamed wrapper, not removal of
the existing alias layer to restore normal header topology. The old setup and
next-position source-shape exhaustion is unrelated and is not reopened.
No prior natural owner-header normalization experiment is documented in these
ledgers. The new concrete premise is the demonstrated current canonical
LNK2005 from Action Alert's ordinary selection-1 duplicate when natural real
callers in Game Sound and Observer emit the same header API as selection 2.
`source/math/real_math.h` owns the actual APIs and bodies:
- line1027: `__inline real_vector3d *vector_from_points3d(...)`;
- line1049: `__inline real magnitude_squared3d(...)`;
- line1090: `distance_squared3d`, which genuinely invokes both above;
- line1359: `__inline real real_random_range(...)`.
`source/ai/actions.h:12` already includes that header, and Action Alert also
explicitly includes it. The existing distance callers in update, next-position
and perform plus the random-range caller in perform naturally expose all
three helpers. No synthetic caller, forced-inline annotation, compiler flag,
header edit, or code-generation control is required.
## Exact minimal edit
Apply only the three replacement records in
`scratch/action_alert_natural_math_owners_20260905.edits.json` to
`source/ai/action_alert.c`:
1. Remove three pre-include `#define original original_inline` aliases.
2. Remove their three corresponding `#undef` lines.
3. Remove the duplicate ordinary external definitions of real_random_range,
vector_from_points3d and magnitude_squared3d.
Every include, gameplay body, private declaration, type, constant, enum, and
call remains untouched. This restores existing owner APIs; it does not add
inlining or relax the January point_from_line3d schedule.
## First natural gate and full runtime audit
Commands from the canonical worktree, with ordinary per-TU flags:
```text
python tools/campaign/gate.py source/ai/action_alert --all --out scratch/action-alert-natural-math-baseline-replay-20260905.obj --forbid-emitted-symbol _point_from_line3d
python tools/campaign/gate.py source/ai/action_alert --edits scratch/action_alert_natural_math_owners_20260905.edits.json --all --out scratch/action-alert-natural-math-first-20260905.obj --forbid-emitted-symbol _point_from_line3d
python scratch/action_alert_natural_math_owner_audit_20260905.py
python scratch/action_alert_natural_math_link_review_20260905.py --scan --link
```
Exactly one untouched baseline replay and one natural changed compile were
used. No source retry followed. Both gates report 11E/1R/0U and no forbidden
point_from_line3d owner. The residual next-position remains 768 candidate
versus 784 target padded bytes; its complete section fingerprint is unchanged.
The complete all-section audit includes anonymous/code/data/debug/directive
sections, COMMON and symbol inventories, offsets, storage and type, section
flags and selection, and ordered relocations including full target-symbol
metadata. It does not mistake function-only equality for whole-owner equality.
- Target:12 code owners,6 data owners.
- Actual baseline and untouched replay:14 code owners,6 data owners.
- Candidate:12 code owners,6 data owners.
- No new runtime owner, no COMMON owner or COMMON change.
- The only removed runtime owners are `_vector_from_points3d_inline` and
`_magnitude_squared3d_inline`, each48 padded bytes. Neither exists in January.
- The only inherited runtime changes are COMDAT selection1 to selection2 for
the three actual public math helpers. Each is otherwise completely identical
in bytes, ordered relocations, symbol metadata/offsets and section flags.
- All other inherited code, data and runtime records are completely identical,
including the parked body; there is not even a compiler-local-label change.
- Actual baseline and untouched PID replay have no runtime differences.
- COFF runtime header characteristics are unchanged. Debug/output-path records
are separately preserved in the raw audit and are not runtime credit.
The target selection1 metadata is retained in the audit. This packet does not
patch csplit metadata or claim that its original source-object selection was
recovered from the linked January image. The natural header source generates
ordinary select-any owners and repairs real current-source ownership debt.
## All-owner census and ordinary link checks
The census parsed all833 configured January targets and572 actual canonical
bases, with zero errors. Among current base definitions:
| Helper | Base owners | Selection-1 owners |
|---|---:|---|
| vector_from_points3d |51|Action Alert only|
| magnitude_squared3d |67|Action Alert only|
| real_random_range |8|Action Alert only|
Every other base copy is selection2. Both alias-named extras exist only in
Action Alert; there are zero incoming relocations to either alias across all
target and base objects. Their removal cannot leave a referenced alias owner
unresolved.
All magnitude and random-range copies have the January size/hash. Fifty
vector copies have the January hash; the existing Breakable Surfaces copy has
hash `a947d351c7b0abf577d0b6e41f02a4f0a7418104d3099e62f6835a862b355b57`.
That frozen48-byte/zero-relocation copy differs only by moving the argument
load `mov edx,[ebp+8]` before rather than after the first FLD. All26 decoded
instructions and dataflow otherwise agree. It uses the actual real_math.h
API and is semantically/ABI equivalent, not byte-identical. No expression or
source change was made to suppress or force this ordinary pooled copy.
Ordinary XDK Link.Exe `/NOLOGO /DLL /NOENTRY /NODEFAULTLIB` tests with unchanged
frozen natural Action Alert + Game Sound and Action Alert + reviewed Observer,
each in both input orders, now have no LNK2005. The previous actual-baseline
Game Sound pair has LNK2005 in both orders in its durable rejection ledger.
The new runs still end in expected unrelated LNK1120 unresolveds (57 for Game
Sound,40 for Observer), and none produces a DLL. Thus this is a passed bounded
duplicate-symbol check, not successful whole-link proof. No /FORCE, /IGNORE,
object patches, synthetic stubs, header shim or linker accommodation is used.
## Frozen evidence
All following paths are relative to the canonical worktree.
| Artifact | SHA-256 |
|---|---|
| scratch/action-alert-natural-math-target-20260905.obj |351ff0de9ddfea9b3c03a9038abab75b0c4b48290443b33e09c33105b41ab61a|
| scratch/action-alert-natural-math-before-20260905.obj |b82fd9a16f40c01fc45ac810240ad9ea9e02287ae629827c01306b17f7e38d30|
| scratch/action-alert-natural-math-baseline-replay-20260905.obj |35bbfb9e7f3f0690af27b6772189d4360e1fbddad71ba81b57b98f027397f549|
| scratch/action-alert-natural-math-first-20260905.obj |62ffdb7cd1e7c082b6f03216d4c10bc3437a8f3d0d11d01ba454b0981fc8ac44|
| scratch/action_alert_natural_math_before_20260905.c |3a57d07c1c1173cb26903ca40da1b4fc714d11779e65e34bfe8e6f7a40e1ce6d|
| scratch/action_alert_natural_math_candidate_20260905.c |27d9268805f0e92024ec10304fbb3dee1398d54055cfb01219d8796f7f0f28af|
| scratch/action_alert_natural_math_owners_20260905.edits.json |a91e4428803f9cc697e4d06b52abfbe82c69f8425b9d518360dede80a9624374|
| scratch/action_alert_natural_math_owner_audit_20260905.json |9c4bcb9e971e188518b8dda1a9ea20d894bfbd56f12e7fd7e7426902a5eef889|
| scratch/action_alert_natural_math_all_owners_20260905.json |737872e034ecd35ee428462306ed87cf6b12f244413944be5625f4701c07ac92|
| scratch/action_alert_natural_math_ordinary_links_20260905.json |f1b5c5d828edd62cffa6882d9469c59f50c14a2d985547394852c9a4c169d682|
## Canonical integration status
Root read the complete source packet, manifest and primary scratch ledger,
then applied only the minimal Action Alert source removals. The full canonical
Ninja build passes. The complete stable sweep reports 6,217 exact functions
out of 8,245, with zero exact losses and no new exact credit from this cleanup.
These are actual canonical results, separate from the frozen scratch results
above.
Full actual runtime-owner comparison and the remaining tooling/tests are
pending at the time this durable ledger is authored. Root must complete those
checks before treating this batch as fully verified. The existing residual
and park remain unchanged, and no whole-object completion or new-byte claim
is made. The ordinary two-object linker diagnostics above remain bounded
duplicate checks, not a successful full-game link.
@@ -1,95 +0,0 @@
# action_alert.obj — opus5 150K lane, wave w2 (2026-09-14)
## Baseline and final
| gate | result |
| --- | --- |
| baseline real file | `== exact 11 residual 1 unwritten 0`, guard passes |
| final real file (unchanged) | `== exact 11 residual 1 unwritten 0`, guard passes, park drift 0, fake scan 0 leads |
`git diff --stat -- source/ai/action_alert.c` is empty. No credit is claimed.
## `_action_alert_next_position` (parked, register-allocation)
Target 784 / 18 relocs / nsha `677fd6cc1a9e66de`; real-file candidate 768 / 18 / `4e16c85b544ded33`.
### New evidence
Aligned diff of the real file:
- January preloads `or eax,0xffffffff` before the `vehicle_passenger` test (3 B).
- January's NONE returns are 32-bit (`mov eax,0xffffffff` on the no-position tail, `or eax,-1` in the guard return block at +0x2f8).
- The encounter guard jumps past that block because EAX already holds -1.
Measured VC7 facts:
- `return NONE;` in a `short` function emits a 16-bit `or ax,0xffff`. So does `return result;` when result is constant-propagated (d7).
- Only an assignment to a `short` result variable emits the 32-bit form.
The function is therefore single-exit (brief section 9 single-exit law). The earlier park probes had not tested this with the complete assignment topology.
### Shapes
| shape | result |
| --- | --- |
| v1: init `result = NONE`, three-way `&&` guard, `if (any) {...}`, `result = next` | 784/18 `2c7d5520` (shrink-wrapped pushes, result in ECX) |
| v2: nested guard; v3: result merged into `next_position_index` | identical to v1 |
| v4: no init, `else { result = NONE; }`, `if (!any) result = NONE;` | 768/18 `6ac81fbc` (guards merged into one block, no preload) |
| v5: nested if/else; n1: `else if (encounter == NONE) result = NONE;` | identical to v4 |
| diagnostic d5: guards `return NONE`, init, no-position store, loop `return` | 1 instruction off (`or ax,0xffff`) |
| diagnostic ea: ec without the no-position store | frame 0x24, wrong |
| **ec / d10** | **strict EXACT 784/18 `677fd6cc`; whole TU 12/12 exact** |
The ec / d10 body:
```c
short result = NONE;
if (actor->input.vehicle_passenger || move_position_order == _move_position_order_none)
{
result = NONE; /* redundant with the initialiser */
}
else if (actor->meta.encounter_index != NONE)
{
/* encounter/squad lookup, swarm assert */
if (move_position_order == _move_position_order_repeat && current_position_index != NONE)
result = current_position_index;
else
{
/* availability scan (unchanged) */
if (!any_position_available)
result = NONE; /* redundant with the initialiser */
else if (move_position_order == _move_position_order_random)
result = choose_random_array_element(...);
else
{
/* clamp + do/while traversal (unchanged) */
result = next_position_index;
}
}
}
return result;
```
Scratch candidate: `scratch/workers/model_animations_actor_stimulus_etc/aa_ec.c` (patch `aa_ec.patch`). Its checks:
- the 11 siblings stay exact;
- the `_point_from_line3d` guard passes;
- parkcheck reports drift only on the park itself;
- fake scan finds 0 leads (the scanner does not detect redundant stores);
- the function set is unchanged.
### Disposition
This is not proposed as an admissible reopen. Exactness requires two NONE stores that the initialiser already guarantees. Every non-redundant spelling measured returns to a different fixed point (v1-v5, n1, d11). Brief section 5 prohibits redundant stores. Previous verifiers rejected the `_action_vehicle_desirable` and `_action_obey_command_begin` re-stores. `_extract_sequence` (January-proven redundant logic) was also left parked. This is an **owner-ruling item**.
### Reopen criterion
Either of:
- an owner ruling that admits January-proven redundant default-result stores (land the aa_ec.c body with a disclosure comment and unpark);
- a non-redundant spelling that produces the separate 32-bit NONE materialisations.
## Do-not-repeat
v1-v5, n1, d11 (no-init explicit else after the encounter branch), and ea.
@@ -1,202 +0,0 @@
# ActionCharge: authentic math owners and private names (2026-09-06)
## Result and scope
Starting at published `71128a19fc9707f678f970e380b97ee3181db05e`, remove the
reconstructed ActionCharge-only alias/duplicate-definition packages for
`square_root` and `magnitude_squared2d`. Their existing, identical definitions
in `source/math/real_math.h` now supply the ordinary source call sites.
The header itself is unchanged. No implementation body is rewritten or tuned.
Both helpers still materialize and remain strict exact on the first natural
compile. All **20 inherited exact ActionCharge functions** survive. The
688-byte candidate `action_charge_setup` residual is also byte/relocation
identical to its pre-trial version; `action_charge_perform` remains unwritten.
ActionCharge remains **20 exact / 1 residual / 1 unwritten**, not complete.
Across the campaign all **6,271 strict functions** survive. Meaningful exact
code remains **919,549 bytes**, credited functions **6,225**, matched data
**2,061,024 bytes**, and Matching objects **391/833**. This is source-ownership
progress, **zero new function/byte credit**. The helper bodies were already
counted; changing their owner representation does not count them again.
## Why this is authentic reconstruction
The source previously renamed these two header functions before including
`actions.h`, undefined the aliases afterwards, and supplied duplicate external
C definitions. The historical recovery ledgers explain that those packages
were introduced during reconstruction to reproduce selected-object helper
ownership. They are not original source evidence. Review included the
2026-08-26 math-helper rejection and wave-2 ledgers, real-random recovery,
the 2026-08-28 public residual recovery, and 2026-08-29 helper reintegration.
HCEX's actual debug records authenticate `real_math.h` ownership and report
`WasSpecifiedAsInline=true` and `WasInlined=true` for both functions:
- `square_root`: RVA 0x169DC28, source lines 644-646;
- `magnitude_squared2d`: RVA 0x169DC50, source lines 733-739.
Use `research/tools/DIA2Dump/x64/Release/DIA2Dump.exe -sym <name>` and
`-lines <name>` against
`research/hcea_jun2011_prototype/payload/Halo CE Anniversary (Jun 24 2011)/HCEX_Release.pdb`,
relative to the outer `i-w` workspace. Both line records name
`d:\projects\code\hcex\sources\math\real_math.h`, source MD5
`DE0F004C6EB037D8BE384822F84E002C`.
The January symbol atlas at
`research/halo-symbol-atlas/symbols/halo_ce/4cc87b45f721270392a96f1674ed2b5cd4a7bb4355faeab4531d1cf1884d9520.jsonl`
marks adjacent `normalize2d`, `magnitude3d` and `normalize3d` as inline
functions selected from ActionCharge. It omits these smaller two symbols;
do not misrepresent that omission as direct January name/inline evidence.
January's actual helper bytes and the current header bodies independently
agree; the original selected COFF representation is not proof of a hand-written
external wrapper in the original C file.
Current ordinary call sites justify retention without a fabricated anchor:
`magnitude2d` calls `square_root(magnitude_squared2d(v))`, and `magnitude3d`
calls `square_root` on the three-component squared magnitude. Reuse those
unaltered calls and the existing ordinary header definitions. No new inline
annotation, static-inline conversion, qualifier, forced emission, new source
alias, compiler switch, object patch or comparator exception is introduced.
## Full compiled-object proof
Root's complete before/first ordinary COFF fingerprint comparison finds
**38 COFF sections: 36 unchanged and exactly two ownership changes**
(34 unchanged non-debug sections plus both unchanged debug sections).
Only the COMDAT selection of `square_root` and `magnitude_squared2d` changes
from NODUPLICATES (1) to SELECT_ANY (2). Every executable/data byte, relocation,
symbol name/storage, debug section, COMMON owner and other runtime header
property is unchanged. No section is added or removed. The existing selected
`point_from_line3d` body is unchanged; there is no new copy or inline scheduling
change. The first gate retains both exact helpers and all inherited functions.
Normalized helper hashes remain:
- `square_root`, 16 padded bytes, zero relocations:
`0717923dacb4356a94553a32f0ba8a314904fae6d7d0bfc42ecc4af6f10415b7`;
- `magnitude_squared2d`, 32 padded bytes, zero relocations:
`9d8460618a63e41e020b2ce8071486fe278a849abccad44e5ea444da9700d014`.
The genuine header is unchanged, so no other translation unit needs a source
rebuild. A full Ninja run compiles ActionCharge and regenerates the campaign
reports. In this owner-only phase, all other current base objects and every
selected target remain identical to the frozen baseline. The full strict census has zero
gains and zero losses. **650 tests plus 26 subtests** pass; parks remain
**297 active / zero stale / zero invalid**. Admission retains five existing
rejections and has zero candidates, contradictions or revocations. The source
has zero fake-match scanner leads.
## Current owner census and actual link outcomes
The complete 572-base-object census now finds 60 `square_root` definitions
and 15 `magnitude_squared2d` definitions, **all SELECT_ANY**, with identical
target-correct bytes within each family. There is no remaining NODUPLICATES
definition for either name. `dot_product2d` remains seven identical definitions:
ActionCharge's one NODUPLICATES and six SELECT_ANY copies.
Root's actual VC7 `/DLL /NOENTRY /NODEFAULTLIB` links of the frozen complete
PathObstacles and ActionCharge objects, in both input orders, now report only
`dot_product2d` as LNK2005. The previous `square_root` and
`magnitude_squared2d` duplicate errors are gone. The expected 23 unrelated
unresolved dependencies remain and no successful full engine link is claimed.
No forced-multiple-definition flag, object suppression or synthetic stub
removes the remaining conflict.
## Authenticated private names, without behavior changes
After freezing and verifying the two-owner restoration, a separate `named`
phase replaces exactly two address placeholders in declarations, definitions,
calls and the symbol comment:
| January file offset | Old placeholder | Authentic private name |
| ---: | --- | --- |
| 4304 | `code_000010d0` | `action_charge_find_target_range` |
| 9168 | `code_000023d0` | `action_charge_valid_melee_destination` |
The exact January atlas cited above supplies both names at lines 7 and 14,
each attributed to ActionCharge. HCEX Release DIA independently identifies
both as static and not specified inline, with source lines 821-854 and 860-901
in `d:\projects\code\hcex\sources\ai\action_charge.c`. The source was already
static; the two matching `config/symbols.json` entries now record the genuine
names and `static: true` rather than csplit's default external ownership.
The source argument types and all executable statements remain unchanged.
In particular, the melee destination's second argument remains
`real_point3d const *goal`: HCEX's `short goal` type metadata is contradicted
by actual January point loads and by the later decompiler's own corrected
body. January reads `[ESI]`, `[ESI+4]` and `[ESI+8]`, while its caller supplies
the prop's center of mass. Do not import the erroneous short signature.
January's helper does not access or write through `melee_target_point`, and
the caller never consumes that discarded local. The HCEA semantic body and
caller corroborate this. Keeping `(void)melee_target_point;` therefore
preserves actual behavior; do not invent an output write or use the parameter
name as proof of missing executable logic. Whole-TU optimization could remove
historical dead stores, so the audit does not claim to prove the original text
never contained an assignment.
Root's full before-name/after-name fingerprint comparison reports all **38
runtime sections unchanged**, once the two authenticated symbol identities
are mapped for comparison. No instruction, literal, data, relocation offset,
addend, debug record or ownership change exists in the base object beyond
those names. The selected target changes names and the two static-storage
records. Across all 1,405 current objects, only the ActionCharge base and
selected target hashes change; every other object is byte-identical. The
full rebuild and strict census again preserve all 6,271 exact functions.
This audit-name mapping is not a comparator exception or new byte credit.
## Remaining boundary
`dot_product2d` has the same authenticated header-inline provenance, but no
live source caller in current ActionCharge. Its real use belongs to the still
unwritten `action_charge_perform`. Do not invent a caller or a retention anchor
to make an uncalled header definition materialize. Its old duplicate-definition
package remains outside this two-owner restoration and needs the genuine
caller closure. Other inherited ActionCharge aliases/prototype
debts are not certified by this patch, and the large residual is not reopened.
This does not claim a complete engine link or whole-object admission. It
reconciles two real duplicate-owner families while preserving all compiled
behavior and accounting. Active Opus implementations and donor worktrees are
untouched. Original binary evidence and temporary link outputs stay local.
## Frozen evidence
Two independent post-implementation audits pass. The owner audit replays the
full before/first COFF comparison, verifies all 572 named-phase base objects,
and runs the actual VC7 diagnostic links in both orders. Its script hard-pins
the imported comparison tools and linker. The separate private-name audit
checks all section and relocation records, caller targets/addends, exact source
and config changes, and all 1,405 current base/selected object hashes.
- Owner replay script: `scratch/action_charge_two_owner_postimplementation_audit_20260906.py`,
SHA-256 `0cc2e805f87cc55920a6bc3202aa284185e36374cbd7eb335e5c1ad15e86fa9d`.
- Independent owner report: `scratch/action-charge-two-owner-postimplementation-audit-final-20260906.json`,
SHA-256 `096b850bd6c00d0e3b8692dea0fbe0ad4e4789556a868a78ab84ed9b8dc9a397`.
- Independent private-name report: `scratch/action-charge-private-name-postimplementation-independent-20260906.json`,
SHA-256 `380291ccd1dd183ca88231dd08b2b68f5aae04639e60fdfffc767006228b11a4`.
These reports are local frozen evidence, not target binaries to distribute.
Both audits preserve the distinction between the remaining `dot_product2d`
link conflict and the two genuinely resolved duplicate-owner families.
- Baseline: `scratch/action-charge-math-owner-before-20260906.json`,
SHA-256 `34ccf1a6d5e32b4348364235802ba488b62c48317e83c45d0317f9fdc93fa4dc`.
- First: `scratch/action-charge-math-owner-first-20260906.json`,
SHA-256 `fd64316616dca63d976bd3c9ca367b9061a69e80ae0c28f27e61a443cdd210d5`.
- Final: `scratch/action-charge-math-owner-final-20260906.json`,
SHA-256 `a81dcaced90b94c6726aa7004b04bb56213c2f2208efe21a8b1898771183d7c5`.
- Before C/base object: `e8096e28a0a821ae5f97b0461c61ae6f828f51037b0cb62b75f5b1d8fb0be366` /
`45c5386b96ba99b67834dd3ffd85e797dc5fdb8385db4cd4ab1e73d006f8df6d`.
- Pre-name two-owner C/base object: `5f182feceec9ccf33d2d8eade40c88db42c697cbfe4d26f5682fce3121bd193a` /
`8c19c85deb223ff89aeaed6ff768e192139e040496c853b1fb91e31eef5c315b`.
- Unchanged math header: `4e640d1be7b2c843dc7c9acb198afcd5d7d390eb310e5e14eedd484cf99535d4`.
- `scratch/action-charge-math-owner-{before,first}-stable-20260906.json`:
6,271 strict functions, zero gains/losses.
- Named final: `scratch/action-charge-math-owner-named-20260906.json`,
SHA-256 `1d4305f1d603e619889f2e94dd282ca40a364d3115ea447ddb579a7c7e257a88`.
- Named final C: `75961e822a451f5d23910def9cc4661e1fbd0bdefa9caa314f776cf0aa21e81d`;
base object: `cecd4d02fc1dbcf8a355ef289899f7230bd3463b3299abe486cb36ee871ef371`;
selected target: `a906f0ec24b0313a056d4a413eaf85ee32f22f02c45878261bcae4521bfa63c8`;
symbols configuration: `57d80ec4aa5d567067f97ac9f4552bba4d85dc777201d480fdadd47f93b06ccf`.
@@ -1,68 +0,0 @@
# `action_charge.obj` helper reintegration — 2026-08-29
## Result
The canonical source now measures **20/22 strict-exact functions** and
**1,984/6,176 strict-exact padded bytes**. This restores the best historical
function frontier while preserving the later public lifecycle, control, and
math-helper recoveries.
Relative to the prior canonical 17/22 object, this is a non-regressing gain of
three functions and 640 padded bytes:
| Function | Padded bytes | Result |
| --- | ---: | --- |
| `_code_000010d0` | 272 | strict exact |
| `_collision_test_line` | 80 | strict exact |
| `_code_000023d0` | 288 | strict exact |
The two remaining functions are classified honestly:
- `_action_charge_perform`: unwritten, 3,248 padded bytes.
- `_action_charge_setup`: readable typed-C residual, 688 candidate bytes and
20 relocations versus the 944-byte/29-relocation target.
## Provenance and recovery
The archived ledger at commit
`677defb9d9b4dd0ac4f20bc2c5dbd53bf3e8d7f5` recorded a measured 20/22
experiment, but deliberately restored its source before commit. Its surviving
notes identified the accepted source topology for the two anonymous helpers:
- the range helper uses zero textually on the left for nonnegative checks and
keeps the suicidal/non-suicidal melee limits in separate branches;
- the destination helper calls the ordinary typed `collision_test_line`
wrapper and brackets the call with the AI-melee collision-user scope.
The readable HCEA donor files were used only to recover behavior and named
field access. A complete typed `action_charge_setup` supplies the natural call
sites needed for VC7 to reproduce the target's internal register calling
convention for both anonymous helpers. No standalone ABI shim, raw offset,
representation cast, object patch, compiler flag, or comparator exception was
used.
## Verification
Commands run from the canonical worktree:
```text
python tools/campaign/gate.py source/ai/action_charge --all
ninja build\base\source\ai\action_charge.obj
python tools/campaign/board.py source/ai/action_charge
python tools/campaign/board.py
git diff --check -- source/ai/action_charge.c
```
Measured output:
```text
UNWRITTEN 3248 _action_charge_perform
residual 944 _action_charge_setup [size 688!=944, relocs 20!=29, sha]
== exact 20 residual 1 unwritten 1 (of 22 listed)
objects 275/619 fully exact; bytes 651111/1922669; fns 4699/8246
```
The campaign totals increase from 650,471 to 651,111 strict-exact padded bytes
and from 4,696 to 4,699 strict-exact functions, with the exact-object count
unchanged at 275.
@@ -1,125 +0,0 @@
# `action_charge.obj` Jonas math-helper recovery rejection — 2026-08-26
## Decision
This frozen wave is **rejected wholesale**. All twelve requested math helpers
reproduced their January padded bytes and relocation identities on the only
ordinary candidate compile, but the candidate also emitted an out-of-line
`_magnitude_squared3d` COMDAT that failed the required linker-level ownership
gate. No production source from this wave is retained and no function or data
credit is claimed from it.
The rejection is useful new evidence: the canonical readable body of
`magnitude3d` is byte-correct after inlining, but spelling it as a call to the
header `magnitude_squared3d` helper causes VC7 to materialize an unwanted
out-of-line copy. A later frozen wave may reopen only with a source topology
that removes that helper reference before compilation.
## Frozen provenance
- Branch: `jonas/action-charge-math-recovery-20260826`
- Base commit: `19ed6902ffa755c9b7c7b01cb926ea8af49d5d14`
- January target object SHA-256:
`76C4C0B62850FFAA42FE5027C3E68B2CC42F7640C1156342C9A9CC4D1125AFEA`
- Clean pre-wave base object SHA-256:
`C0683E96025CBAC9980D9779786C89B69164502336E7ABCA23874EB6835C40CE`
- Frozen candidate source Git blob:
`ffc743d088b2c065a53826300514a4e3122ea500`
- Frozen candidate source physical SHA-256:
`769194E601F4CC9132B8AB90CB825DE07A452896647328179D7D5CC3AC771C08`
- XDK 3911 compiler SHA-256:
`483E00C47BB08D699475A642BCFF15B5B2036350B31C540E88A506BAF101DA11`
- Splitter SHA-256:
`708F957C37CAA66DF90ADE35AD3D1E65E2617E2CC2C8C2CCF68D8BF5CB20E752`
- Candidate object: 3,839 bytes, raw SHA-256
`146B19F6DB3DF4163904B15CD5ABD3AB2E0FBA548CB7F2792E100F3425CE306C`.
- Compiler flags and include search path were unchanged:
`/O2 /Oy- /DDEBUG /Dxbox`.
The source passed the XDK `/Zs` syntax-only gate. A Ninja dry run then exposed
exactly one `CL build\base\source\ai\action_charge.obj` edge. It was invoked
once and its first artifact was preserved before any comparison. No spelling,
declaration, scheduling, compiler, or flag retry followed.
## Strict-exact evidence from the rejected artifact
The hardened comparator reported `all_equal: true` for all twelve new helpers
and the three inherited accepted functions. The twelve helpers contribute 479
meaningful / 544 padded target bytes and six relocations.
| Function | Padded bytes | Relocs | Normalized SHA-256 |
| --- | ---: | ---: | --- |
| `_square_root` | 16 | 0 | `0717923dacb4356a94553a32f0ba8a314904fae6d7d0bfc42ecc4af6f10415b7` |
| `_scale_vector2d` | 32 | 0 | `496d82c0615b4ec0cb4211dd21dfbf507d8959357a60895b9fd4d930b554cf95` |
| `_magnitude_squared2d` | 32 | 0 | `9d8460618a63e41e020b2ce8071486fe278a849abccad44e5ea444da9700d014` |
| `_magnitude2d` | 32 | 0 | `17d586569ec882a34a898e8a589fe5140e8d8b79b85e2fd4e7816dfe6210d3fa` |
| `_normalize2d` | 80 | 3 | `8a5659d5dd494fe2d1d880057d1836185ebfb8e81d9d63362a43e6a2cb287890` |
| `_dot_product2d` | 32 | 0 | `defea20c9a86f0deda350c23260a0e093b08da31a2cbcd5e5c08b17164579415` |
| `_point_from_line3d` | 48 | 0 | `9b763841f8519177ac2d3fd471226972b9fa92cf1e9061e9f92359077dc82741` |
| `_scale_vector3d` | 48 | 0 | `6436919fbdc80b5d5fe76d9c92fa146e5ac7d3b4637c9b75aaa62401f0c54a3b` |
| `_magnitude3d` | 48 | 0 | `114f10e7ee4543446daaaafc859a4ec729ba6c8c74c38825d05bd0f00a42352c` |
| `_normalize3d` | 96 | 3 | `67fbd33f204d0c93cce135fcfd37d4ce5b443642edc6ad99230846b91c893e5b` |
| `_dot_product3d` | 32 | 0 | `d564ef69fd99449fdfc29d59d0d844fb17df91369277eaba9574e7c8c3d2ae68` |
| `_subtract_vectors3d` | 48 | 0 | `1d706c27e470449c1ac16f5c5ff47c98ee2be96426f2b8bb949ba79e9d764cac` |
The inherited sentinels remained strict exact:
| Function | Padded bytes | Relocs | Normalized SHA-256 |
| --- | ---: | ---: | --- |
| `_action_charge_begin` | 80 | 3 | `c78c6feeb10fda8857834f5264614aa976498059590ee08304aef25f21af8bf3` |
| `_action_charge_update` | 80 | 2 | `f2591b2de350c20aefef42c38623875dca494bb46a51d06c6dcd5947b035976e` |
| `_real_random` | 16 | 2 | `50eeeb3dc58069325659d8bc0b6f2c17bb9f4fdce278b2f1a37edb64f4fcc60c` |
Both normalize functions reproduced the target relocation offsets, types,
destinations, addends, and local ownership for `0.0f`, `1.0f`, and the
double-precision epsilon literal. The three emitted target-owned `.rdata`
COMDATs were also strict exact: four bytes at
`__real@00000000`, four bytes at `__real@3f800000`, and eight bytes at
`__real@3f1a36e2e0000000`. They receive no credit because the wave is rejected.
## Ownership contradiction
The candidate additionally emitted `_magnitude_squared3d` as a 48-byte,
zero-relocation, select-any code COMDAT. No candidate relocation refers to the
out-of-line copy. Its normalized bytes are strict exact against the January
canonical owner in `source/ai/action_alert.obj`:
- 48 padded bytes;
- zero relocations;
- normalized SHA-256
`0d04579096eeab3a1c45f87774e1eb20ce4856dbac219f624a855e8c9ae99725`.
That cross-object byte equality is insufficient. The acceleration playbook
allows a candidate-only helper COMDAT only when the ordinary linker discards
it. A direct XDK 3911 link of the January canonical `action_alert.obj` and the
candidate `action_charge.obj` was run in both input orders. Both orders fail:
```text
action_charge.obj : error LNK2005: _magnitude_squared3d already defined in action_alert.obj
action_alert.obj : error LNK2005: _magnitude_squared3d already defined in action_charge.obj
fatal error LNK1169: one or more multiply defined symbols found
```
Thus this is not an admissible discardable-helper representation difference.
The target canonical owner is a no-duplicates COMDAT, and the candidate copy
cannot coexist with it. The wave fails before campaign admission regardless of
the twelve function-byte successes.
## Atomic restoration and reopen criterion
The complete macro/definition package was removed after the ownership failure.
The production source is restored exactly to:
- Git blob `3e76470cf8be066072239845cbf08e37aeffa458`;
- physical SHA-256
`3B798CC7F02BCC50741E88D6C735E9F12780DC4AE645BC69DD48262A6E6D67EB`.
Only this rejection ledger is retained. There is no config, comparator,
compiler, protected-file, source, or matching-label change and no push.
The link failure is genuinely new evidence. A new wave may test one frozen
topology: keep the twelve exact external helper bodies, but express
`magnitude3d` directly as the already-inlined three-component squared-magnitude
expression so the header `_magnitude_squared3d` body is never referenced and
VC7 has no reason to materialize the extra COMDAT. That is a new evidence wave,
not a retry of this rejected source.
@@ -1,183 +0,0 @@
# `action_charge.obj` Jonas math-helper recovery wave 2 — 2026-08-26
## Outcome
This wave recovers twelve math helpers as strict exact and retains all three
previously accepted `action_charge.obj` functions. The object advances from
3/22 to **15/22 strict-exact functions**. The twelve-function gain is 479
meaningful / 544 padded code bytes and six relocations. The object remains
`NonMatching`; none of the seven absent behavioral functions is claimed.
Three target-owned read-only constant COMDATs, sixteen bytes total, are also
present with exact bytes and ownership. The ordinary report may credit those
sections directly; no semantic exception or manual data ledger entry is used.
## Why this is a second evidence wave
The first frozen recovery candidate made all twelve helper bodies byte-exact,
but VC7 also materialized an unwanted select-any `_magnitude_squared3d`
out-of-line helper. A direct XDK link against the January canonical owner in
`action_alert.obj` failed with `LNK2005` in both input orders. That entire
candidate was removed and the rejection is recorded in
`action_charge_obj_jonas_math_helper_recovery_rejection_20260826.md`.
The linker failure supplied genuinely new evidence. Wave 2 therefore froze one
new topology: `magnitude3d` spells the already-inlined three-component squared
magnitude expression directly. Its emitted `_magnitude3d` instructions remain
identical, but no source reference remains from which VC7 can materialize the
rejected helper owner.
## Frozen provenance and compile discipline
- Branch: `jonas/action-charge-math-recovery-v2-20260826`
- Evidence base: `511d251d3765b1caac824a47ed8bf330c677cc85`
- January target object SHA-256:
`76C4C0B62850FFAA42FE5027C3E68B2CC42F7640C1156342C9A9CC4D1125AFEA`
- Clean pre-wave base object SHA-256:
`C0683E96025CBAC9980D9779786C89B69164502336E7ABCA23874EB6835C40CE`
- Frozen candidate source Git blob:
`64818f49f816817883be1621a9737166c7615bb7`
- Frozen candidate source physical SHA-256:
`AF8166FC414F459190CE9720B41E5A3BA7858FAFEEB59B226740CF377D0C7E02`
- XDK 3911 `CL 13.00.9254.1` SHA-256:
`483E00C47BB08D699475A642BCFF15B5B2036350B31C540E88A506BAF101DA11`
- Splitter SHA-256:
`708F957C37CAA66DF90ADE35AD3D1E65E2617E2CC2C8C2CCF68D8BF5CB20E752`
- Flags: `/O2 /Oy- /DDEBUG /Dxbox`; project include list unchanged.
- First candidate object: 3,676 bytes, raw SHA-256
`638CE42681DB10907F0C847F2679806E07091A34085E4EE0D50E1AC3DB1E1E67`.
The frozen source passed the XDK `/Zs` syntax-only gate. Ninja dry-run output
contained exactly one `CL build\base\source\ai\action_charge.obj` edge. That
edge was invoked once, and the first object was copied into the frozen audit
packet before comparison. The accepted object subsequently reports no work.
There was no same-wave source, flag, declaration, scheduling, or compiler
retry.
## Strict function evidence
The hardened comparator requires padded size, normalized bytes, relocation
address/type, resolved destination, addend, and ownership to agree. It reports
`all_equal: true` for the twelve recovered helpers and all three inherited
sentinels.
| Function | Padded bytes | Relocs | Normalized SHA-256 |
| --- | ---: | ---: | --- |
| `_action_charge_begin` | 80 | 3 | `c78c6feeb10fda8857834f5264614aa976498059590ee08304aef25f21af8bf3` |
| `_action_charge_update` | 80 | 2 | `f2591b2de350c20aefef42c38623875dca494bb46a51d06c6dcd5947b035976e` |
| `_square_root` | 16 | 0 | `0717923dacb4356a94553a32f0ba8a314904fae6d7d0bfc42ecc4af6f10415b7` |
| `_scale_vector2d` | 32 | 0 | `496d82c0615b4ec0cb4211dd21dfbf507d8959357a60895b9fd4d930b554cf95` |
| `_magnitude_squared2d` | 32 | 0 | `9d8460618a63e41e020b2ce8071486fe278a849abccad44e5ea444da9700d014` |
| `_magnitude2d` | 32 | 0 | `17d586569ec882a34a898e8a589fe5140e8d8b79b85e2fd4e7816dfe6210d3fa` |
| `_normalize2d` | 80 | 3 | `8a5659d5dd494fe2d1d880057d1836185ebfb8e81d9d63362a43e6a2cb287890` |
| `_dot_product2d` | 32 | 0 | `defea20c9a86f0deda350c23260a0e093b08da31a2cbcd5e5c08b17164579415` |
| `_point_from_line3d` | 48 | 0 | `9b763841f8519177ac2d3fd471226972b9fa92cf1e9061e9f92359077dc82741` |
| `_scale_vector3d` | 48 | 0 | `6436919fbdc80b5d5fe76d9c92fa146e5ac7d3b4637c9b75aaa62401f0c54a3b` |
| `_magnitude3d` | 48 | 0 | `114f10e7ee4543446daaaafc859a4ec729ba6c8c74c38825d05bd0f00a42352c` |
| `_normalize3d` | 96 | 3 | `67fbd33f204d0c93cce135fcfd37d4ce5b443642edc6ad99230846b91c893e5b` |
| `_dot_product3d` | 32 | 0 | `d564ef69fd99449fdfc29d59d0d844fb17df91369277eaba9574e7c8c3d2ae68` |
| `_subtract_vectors3d` | 48 | 0 | `1d706c27e470449c1ac16f5c5ff47c98ee2be96426f2b8bb949ba79e9d764cac` |
| `_real_random` | 16 | 2 | `50eeeb3dc58069325659d8bc0b6f2c17bb9f4fdce278b2f1a37edb64f4fcc60c` |
The two normalize functions reproduce all six January DIR32 relocation slots:
- `_normalize2d`: `+0x21`, `+0x2E`, `+0x46`;
- `_normalize3d`: `+0x2C`, `+0x39`, `+0x59`;
- destinations in both cases: the double epsilon, `1.0f`, and `0.0f`, with
zero addends and target-identical local ownership.
## Whole-object ownership audit
The January target owns 22 code sections. The candidate owns exactly fifteen,
all of which are target-owned and strict exact. It has **no candidate-only code
owner**. The seven target-only functions are exactly:
1. `_code_000010d0`
2. `_action_charge_control`
3. `_action_charge_is_leaping`
4. `_collision_test_line`
5. `_action_charge_perform`
6. `_code_000023d0`
7. `_action_charge_setup`
The candidate owns no `.data`, `.bss`, COMMON symbol, or other writable runtime
storage. Its only defined runtime non-function owners are these target-owned
select-any `.rdata` COMDATs:
| Symbol | Bytes | Relocs | Normalized SHA-256 |
| --- | ---: | ---: | --- |
| `__real@00000000` | 4 | 0 | `df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119` |
| `__real@3f800000` | 4 | 0 | `e00e5eb9444182f352323374ef4e08ebcb784725fdd4fd612d7730540b3e0c8c` |
| `__real@3f1a36e2e0000000` | 8 | 0 | `564be83b341e452f4492fa81f7d5f215f446119f583c33a94b619cd1ee7287fd` |
The `_magnitude_squared3d` symbol and section are absent. Diagnostic XDK links
of the complete January `action_alert.obj` target with the candidate object
succeed in both input orders under `/FORCE:UNRESOLVED` (exit 0); the expected
unresolved project dependencies remain warnings, while the wave-1 `LNK2005`
and `LNK1169` ownership failures are gone.
## Source and policy review
The retained code is ordinary typed C. It keeps one parameter per line, uses
`void` on its own line for the no-argument function, and gives every function
an explicit return. It introduces no assembly, `register`, `volatile`, pragma,
attribute, forced inlining, barrier, byte forcing, raw address, raw object/tag
cast, pointer/integer reconstruction, union pun, or undefined behavior. No
protected Units or game-engine file is touched.
The isolated tooling suite passes 179/179 tests. `git diff --check` is clean,
the candidate object dry run reports no work, and the changed production path
is only `source/ai/action_charge.c`. Whole-campaign reports, regression
snapshot/check, and cumulative-tree validation are recorded in a committed
replay after integration.
## Committed cumulative replay
The evidence branch was merged into `jonas/units-integration-20260824` at
`b08d30922a82ee675400a8d9f78aaef99dbe8dcf`. The cumulative compiler replay
produced a 3,676-byte object with raw SHA-256
`C11BB3BF615299968C93F4BB4857A12B4955D11B8255C6A45F99508E63595CDF`.
Raw object hashes differ normally with the COFF timestamp; hardened comparison
again reports `all_equal: true` for all fifteen code owners and all three
constant owners. The cumulative owner table still contains exactly fifteen
target-owned functions and no candidate-only function, `.data`, `.bss`, or
COMMON owner.
The regenerated ordinary report records the unit as:
- 15/22 matched functions;
- 643/6,032 meaningful code bytes;
- twelve functions and 479 code bytes above the pre-wave 3/22 state;
- 256 target data bytes represented as one aggregate report section.
The three constant COMDATs are independently strict exact, but the ordinary
report does not expose partial per-COMDAT data credit inside its aggregate
`.rdata` measure. The semantic-data gate intentionally permits incomplete-unit
credit only when a verified owner spans the entire remaining data gap. These
three owners span 16 of 256 bytes, so matched campaign data remains unchanged;
no exception, accounting change, or overstated 16-byte claim is introduced.
A committed `tools.regression_gate` snapshot/check for
`source/ai/action_charge` reports:
- all fifteen functions `still_exact`;
- `changed_nonexact: []`;
- `newly_exact: []` after the committed snapshot;
- no failures and no warnings.
The cumulative verification set passes:
- `halobetacache_build libcmt_build`: no work after the replay object;
- semantic report: 470 units, 4,288 functions evaluated, 4,210 accepted exact,
and zero unit errors;
- campaign progress: 507,503 / 2,198,102 code bytes, 4,184 / 11,060
functions, and 1,836,756 / 4,176,062 data bytes;
- Halo category: 494,589 / 1,770,166 code bytes and 4,017 / 7,574 functions;
- tooling tests: 179/179 passed;
- `git diff --check`: clean.
The fail-closed admission audit reports zero completion candidates and zero
revocations. Its sole contradiction is the already-known
`source/shell/shell_xbox` completion label, whose `_main` false positive is
independently rejected by existing semantic policy; this wave neither touches
nor changes it. No push is performed.
@@ -1,109 +0,0 @@
# `action_charge.obj` Jonas public residual recovery — 2026-08-28
## Result
This bounded recovery starts from cumulative integration commit
`9c006fff3ae73abaa3d83ca208aa20ab90785163` and the existing 15/22 exact
partial. It recovers the two remaining public functions previously proven by
the archived full-TU experiment:
| Function | Padded bytes | Relocations | Normalized SHA-256 |
| --- | ---: | ---: | --- |
| `_action_charge_control` | 544 | 6 | `5be694b750701983145a5c29336fe5dc3ffe97cd7b53111013694012215e2095` |
| `_action_charge_is_leaping` | 80 | 3 | `9ddd3d1296e581d078e6958f4039f9bde736698282368436b60d130978e7b275` |
| **Wave gain** | **624** | **9** | |
All fifteen inherited functions remain strict exact. `action_charge.obj`
advances from 15/22 and 720/6,176 padded bytes to 17/22 and 1,344/6,176 padded
bytes. It remains `NonMatching`.
The retained source Git blob is
`7a87fe26032376f229e251ab6cf0f2f7f6f945da`. The January split-object
SHA-256 is `76c4c0b62850ffaa42fe5027c3e68b2cc42f7640c1156342c9a9cc4d1125afea`.
## Provenance and scope
Every current action-charge ledger and the archived frontier ledger at commit
`677defb9d9b4dd0ac4f20bc2c5dbd53bf3e8d7f5`, ledger blob
`bbb630a9fc673dab443948add44b8457e3b465e4`, was read before emission. That
archived full-TU experiment independently measured both retained source shapes
strict exact, then restored its production source because
`action_charge_perform` and `action_charge_setup` remained non-exact. The
current pre-wave source blob was
`64818f49f816817883be1621a9737166c7615bb7`.
The readable semantic oracle is HCEA commit
`570c83fd9c365dad6f2a3e7041705d5b84c7847c`:
- `src/blam/ai/action_charge_control.c`, blob
`b29413ab09c0db5049d66b62cacf7e6d717c3149`, payload SHA-256
`4fd1e1de0fbf920e95a2058ba55d8de27893a99981cfb8119178876970bd2451`;
- `src/blam/ai/action_charge_is_leaping.c`, blob
`38fc8c40afa3cc720f5d8f784c22da25150febcc`, payload SHA-256
`c6857cdfcc0225c97dd8bdfe56a8f5a8ea721881a48ca58e71b8af0911b7a7d5`.
January COFF, current typed actor layouts, and existing exact-history evidence
remain authoritative where the later donor evolved. The source uses
`actor_get` and `actor_definition_get` rather than raw object casts. The only
new header is the existing typed `game/game.h` interface for `game_time_get`;
no shared declaration or storage owner changes.
`action_charge_control` restores the target's exact priority-local topology,
including priority 5 initialized in the fallback block and priority 7 written
only in the locked-aiming arm. It then applies typed crouch, leap, panic,
movement, and combat orders through named actor fields. `action_charge_is_leaping`
forms one typed charge-state pointer, keeps one shared result return, and tests
the authenticated 30-tick leap window.
## Bounded five-body disposition
The historical frontier also proved three private/collision bodies exact:
`_code_000010d0`, `_code_000023d0`, and `_collision_test_line`. They were
included in the one natural recovery packet to test whether the current partial
could own them without the two excluded large callers.
VC7 correctly eliminated all three because the current TU contains no lawful
caller after `action_charge_perform` and `action_charge_setup` are omitted.
They were therefore `UNWRITTEN`, not byte residuals. Their candidate bodies,
prototypes, macro rename, constants, and include support were all pruned before
the retained replay. No synthetic caller, anchor, forced emission, external
ownership rewrite, or zero-credit support body is retained.
The archived ledger already proves the two large residuals are structural:
- `_action_charge_perform` is 3,248 padded bytes with the exact size, frame,
relocation count, and destination sequence but a recovered source topology
that moves 82 of 84 relocation addresses;
- `_action_charge_setup` is 944 padded bytes and needs the original shared-exit
CFG rather than another early-return or `goto` spelling.
Do not retry those fixed points without authentic debug-scope/source topology.
Reopen the three dead helpers only when an admissible authentic caller causes
ordinary target-compatible materialization.
## Policy and validation
The retained code is typed readable C. Each parameter occupies its own line;
both functions end in explicit returns. It contains no assembly, `volatile`,
`register`, pragma, intrinsic, optimizer barrier, raw address or byte-offset
access, pointer/integer reconstruction, punning, inactive-union access,
undefined behavior, synthetic retention, object-byte forcing, comparator
exception, configuration edit, or compiler-flag change.
The isolated retained-only `/Zs` parse and normal XDK 3911 replay passed. Direct
strict comparison returned `all_equal: true` for every one of the 17 emitted
target functions. After copying the identical source blob to cumulative
integration, one normal Ninja edge rebuilt
`build/base/source/ai/action_charge.obj` and
`python tools/campaign/gate.py source/ai/action_charge --all` reported:
```text
exact 17 residual 0 unwritten 5
```
The five unwritten target functions are exactly the two documented large
fixed points and the three pruned dead helpers. `git diff --check` is clean.
Only `source/ai/action_charge.c` and this Jonas-owned ledger are added by this
wave. Units, Vehicles, Claude-active source, shared headers, configuration,
semantic policy, parked records, comparator code, and object status labels are
untouched.
@@ -1,243 +0,0 @@
# `action_charge.obj` Jonas real-random recovery
## Result
This bounded recovery starts from cumulative commit
`c07cbfb90b8bf67a86cb28bc6f3a1dffed70a486`. It adds the ordinary typed
`real_random` wrapper already authenticated by January COFF, the canonical
math interface, and the archived full-TU action-charge experiment.
`_real_random` is strict exact on the frozen recovery compile. The two
inherited lifecycle leaves remain strict exact, so `action_charge.obj`
advances from 2/22 to 3/22 exact functions. It remains `NonMatching` and
receives no data credit.
| Function | Meaningful / padded | Relocs | Normalized SHA-256 | Disposition |
| --- | ---: | ---: | --- | --- |
| `_real_random` | `13 / 16` | 2 | `50eeeb3dc58069325659d8bc0b6f2c17bb9f4fdce278b2f1a37edb64f4fcc60c` | newly exact |
| `_action_charge_begin` | `80 / 80` | 3 | `c78c6feeb10fda8857834f5264614aa976498059590ee08304aef25f21af8bf3` | inherited exact |
| `_action_charge_update` | `71 / 80` | 2 | `f2591b2de350c20aefef42c38623875dca494bb46a51d06c6dcd5947b035976e` | inherited exact |
The accepted object total is 164 meaningful bytes, 176 padded bytes, and
seven relocations across three functions.
## Provenance and recovery status
This is not represented as a compiler-naive first shot. Archived commit
`677defb9d9b4dd0ac4f20bc2c5dbd53bf3e8d7f5` records a 20/22 full-TU
experiment in `docs/object_matching_logs/action_charge_obj.md`, blob
`bbb630a9fc673dab443948add44b8457e3b465e4`. That experiment independently
compiled `_real_random` strict exact, then restored the translation unit
because the large `action_charge_perform` and `action_charge_setup` bodies
remained non-exact. This wave recovers only the already authenticated wrapper;
none of the rejected large-function source, scheduling hypotheses, or
temporary data owners is reused.
The current two-leaf checkpoint is blob
`0ab1bc6655eda440815fd2c185160f8ba96de081`, last clarified by commit
`4053598caa60b40da0e660c138708f85e2a8550b`. The baseline source blob is
`d34d932679150399937475d915ade404f7f94ac9`; the frozen recovery source blob is
`3e76470cf8be066072239845cbf08e37aeffa458`.
The January split object has SHA-256
`76c4c0b62850ffaa42fe5027c3e68b2cc42f7640c1156342c9a9cc4d1125afea`.
The compiler is Microsoft 32-bit C/C++ Optimizing Compiler `13.00.9254.1`,
SHA-256
`483e00c47bb08d699475a642bcff15b5b2036350b31c540e88a506baf101da11`,
with the unchanged XDK 3911 C command and flags.
## Typed interface and source shape
Current `source/math/real_math.h`, blob
`f14567675126171a4b1c2d3052e8058c68c029c2`, declares both callees and owns
the canonical inline behavior:
```c
real real_random(
void)
{
return real_seed_random(
get_global_random_seed_address());
}
```
`actions.h` is the first transitive route to `real_math.h` in this TU. The
inline name is therefore narrowly macro-renamed to `real_random_inline` after
`cseries.h` and before `actions.h`, then undefined after the existing include
list. The explicit external definition above is retained in the public-code
section. No shared-header declaration, duplicate prototype, synthetic caller,
or emission anchor is introduced. Direct symbol inspection proves that no
`_real_random_inline` owner or reference is emitted.
Both dependencies are strict exact in complete
`source/math/random_math.obj`:
- `_get_global_random_seed_address`: 53 meaningful / 64 padded bytes, seven
relocations, normalized SHA-256
`978f6fa37835b338c2088717e09aa32a8e80f074c0c52043c3ee74f9e0c3e9a1`;
- `_real_seed_random`: 48 meaningful / 48 padded bytes, two relocations,
normalized SHA-256
`ad693d3107104928cec2113aa91e03214c37eced08553a417d7f90191ccec3b4`.
## Strict relocation and ownership evidence
The target and candidate padded bytes are identical. Both relocations are
zero-addend REL32 entries:
| Offset | Destination |
| ---: | --- |
| `+0x01` | `_get_global_random_seed_address` |
| `+0x07` | `_real_seed_random` |
The target-authoritative census is 3/22 strict-exact functions against 6,032
meaningful / 6,176 padded target code bytes. The candidate adds exactly one
NODUPLICATES code COMDAT owner `_real_random`, plus undefined references to
the two typed callees and VC7's ordinary compiler-generated `__fltused`
linker marker. The latter is an undefined external, not a runtime owner; the
same natural marker is present in the accepted `action_alert.obj` real wrapper.
There is no owner removal and no candidate `.rdata`, `.data`, BSS, COMMON, or
other runtime non-code section. All fifteen target read-only COMDATs, 243 raw
payload bytes / 256 progress bytes, remain uncredited.
The generic baseline regression gate reports exactly `_real_random` as
`NEWLY_EXACT`, both lifecycle functions as `still_exact`, and
`changed_nonexact: []`. Its nonzero result is confined to the expected new
symbol set plus compiler debug changes in `.debug$F` and `.debug$S`; no
adjudication or comparator exception is added.
## Compile and policy record
The frozen source passed the XDK `/Zs` parse-only check. The subsequent Ninja
dry run exposed exactly one
`CL build\base\source\ai\action_charge.obj` edge. That edge was invoked once,
reported exactly `[1/1] CL build\base\source\ai\action_charge.obj`, and its
output was immediately preserved as
`build/audit/action_charge_real_random_recovery_20260826.obj`, 1,405 bytes,
raw SHA-256
`365281ea87d2f74746742f68b8928223b05bee7abe16d5207734e761ba02ebb5`.
No source spelling, include position, declaration, retry, scheduling tune,
compiler flag, or second candidate compile followed.
The retained source is readable typed C with `void` on its own parameter line
and a direct explicit return. It contains no assembly, `volatile`, `register`,
pragma, intrinsic, attribute, forced inlining, barrier, raw offset or address,
pointer/integer reconstruction, cast or union pun, inactive-union access,
undefined behavior, synthetic caller or anchor, object-byte patch, comparator
exception, or alternate compiler flag.
Direct hardened comparison reports `all_equal: true` for the new wrapper and
both sentinels. The target object dry run reports no work after preservation,
`git diff --check` and the changed-path/protected-file review pass, and all 179
tooling tests pass. Whole-campaign reports and complete Halo/libcmt validation
are deferred to the corrected cumulative-HEAD closure so this isolated
worktree does not rebuild unrelated historical outputs.
Only `source/ai/action_charge.c` and this Jonas-owned ledger are changed. The
approval-gated Units package and its protected neighbors remain frozen. No
push is performed.
## Committed-state replay
Implementation-and-ledger commit
`8ed01222d5e1eec38b3a7a8801c05e52926c753e` was clean before replay. Its
committed source blob remains
`3e76470cf8be066072239845cbf08e37aeffa458`. The clean selected-unit manifest
is `build/audit/action_charge_real_random_committed_20260826.json`, 250,738
bytes, SHA-256
`0fa2c07f0859a868f37e10c0ae0f6094d02a265d26522a00d8f025eaf90945ac`.
The resolved generated object path was verified to remain inside the isolated
worktree. `build/base/source/ai/action_charge.obj` was literally deleted, its
absence was confirmed, the Ninja dry run exposed exactly one expected compiler
edge, and the normal rebuild reported exactly
`[1/1] CL build\base\source\ai\action_charge.obj`. The preserved committed
replay is 1,405 bytes with raw SHA-256
`97d6ffb9f5f320010affd46b659c0de3a2012be0a8376e2e8c9b6697fb9e696f`.
The replay and frozen recovery objects have identical length and differ at
exactly COFF header offsets `+0x4` and `+0x5`, compile-timestamp bytes. Every
modeled section, symbol, meaningful and padded byte, and relocation is
identical. Direct hardened comparison again reports `all_equal: true` for all
three accepted functions. The committed manifest check has no failures or
warnings, no newly exact or changed-nonexact entry, and places
`_action_charge_begin`, `_action_charge_update`, and `_real_random` in
`still_exact`. Both the selected object dry run and normal invocation report
no work afterward.
This section is the only tracked post-implementation change. No push is
performed.
## Corrected cumulative-HEAD closure
The cumulative branch was fast-forwarded from
`c07cbfb90b8bf67a86cb28bc6f3a1dffed70a486` through the isolated
implementation and replay-ledger commits to
`450249b0ead2989462f6e7d1218f0ad9ebd0a7d6`. The implementation source blob
remains `3e76470cf8be066072239845cbf08e37aeffa458`; no protected source, shared
header, storage declaration, configuration, semantic ledger, parked manifest,
or object-status file changed during integration.
From that clean corrected HEAD, the resolved cumulative
`build/base/source/ai/action_charge.obj` was verified inside the cumulative
worktree and literally deleted. The Ninja dry run exposed exactly one expected
compiler edge, and the normal rebuild reported exactly
`[1/1] CL build\base\source\ai\action_charge.obj`. The preserved corrected
replay is
`build/audit/action_charge_real_random_corrected_head_replay_20260826.obj`,
1,393 bytes, raw SHA-256
`f78c30bbff7d78d9b9f576a4b36a1bd5831ef8ba54e1f373b3ebf9102477cb13`.
Direct hardened comparison again reports `all_equal: true` for
`_real_random`, `_action_charge_begin`, and `_action_charge_update`. The
accepted corrected delta remains exactly one function, 13 meaningful bytes,
16 padded bytes, and two relocations, with no changed non-exact function and
no runtime-data gain or regression.
The isolated and cumulative raw objects are root- and timestamp-sensitive.
VC7's `.debug$S` record embeds the absolute output-object path: it is 194
bytes in the isolated `action-charge-three-leaf-20260821` worktree and 182
bytes in `units-integration-20260824`, exactly accounting for the twelve-byte
object-size difference. After report regeneration, the cross-worktree
manifest check reports only this `.debug$S` difference. All three accepted
functions are `still_exact`, `changed_nonexact` is empty, and every modeled
production code and runtime non-code record is stable.
The corrected clean manifest is
`build/audit/action_charge_real_random_corrected_head_manifest_20260826.json`,
250,738 bytes, SHA-256
`a5a9efe588d81de5e8dce55df5a44cbe4627dc0c09500e67f9df4db6da0a616e`.
Its no-build self-check has no failures or warnings, no newly exact or changed
non-exact entries, and retains all three functions in `still_exact`.
Regenerated corrected artifacts are:
- `action_charge_real_random_corrected_head_report_20260826.json`: 1,573,815
bytes, SHA-256
`4ccad6c2d5fd4d48c0e4755d3dd07fddac6a800d15b41620bc01667440b4d6fe`;
- `action_charge_real_random_corrected_head_semantic_20260826.json`: 2,972,764
bytes, SHA-256
`2408321ac6feeab0c9b2ff8de676fd408d3cdb369419ec3afcd7e896f6590a9d`;
- `action_charge_real_random_corrected_head_admission_20260826.json`: 384
bytes, SHA-256
`f0aa45c22cc604fb74abc388b06dd79a27e7a0b498a5d28837a9e785680c9a36`;
- `action_charge_real_random_corrected_head_parked_20260826.json`: 3,950
bytes, SHA-256
`cfac03aea5be607c19a6a40bcdbc1164e71ffb0179602af4bd37b3fd50e26d1d`.
The hardened semantic audit evaluates 4,246 functions, accepts 4,165 exact
functions, and reports zero unit errors. Campaign progress is 375/833 objects,
4,139/11,060 exact functions, 502,200/2,198,102 meaningful code bytes, and
1,835,088/4,176,062 matched data bytes. Halo progress is 273/468 objects,
3,972/7,574 exact functions, 489,286/1,770,166 meaningful code bytes, and
1,829,896/3,923,451 matched data bytes. `action_charge.obj` is 3/22 exact with
164 meaningful exact code bytes and no data credit.
Admission remains zero candidates and zero revocations; only the inherited
`source/shell/shell_xbox` completion-label contradiction remains. Parks remain
three active / zero stale / zero invalid. The selected object and complete
Halo/libcmt dry runs and normal invocations all report no work, and all 179
tooling tests pass.
The approval-gated Units package remains frozen. This closure does not
constitute its required exact approval phrase and authorizes no edit to Units
or its protected neighbors. No push is performed.
@@ -1,165 +0,0 @@
# `action_charge.obj` Jonas two-leaf wave
## Result
This bounded source-only wave starts from authoritative integration commit
`3f2ba63dfb1258f7e36baaec69fe495bcefdc20f`. The three permitted public
action-charge leaves were emitted together in exactly one natural candidate
compile. `_action_charge_begin` and `_action_charge_update` were independently
strict and are retained. `_action_charge_is_leaping` missed and was removed
immediately, without a spelling, declaration, control-flow, or compiler-control
retry.
`action_charge.obj` advances from 0/22 to 2/22 exact functions and remains
`NonMatching`.
| Retained function | Meaningful | Padded | Relocations | Normalized SHA-256 |
| --- | ---: | ---: | ---: | --- |
| `_action_charge_begin` | 80 | 80 | 3 | `c78c6feeb10fda8857834f5264614aa976498059590ee08304aef25f21af8bf3` |
| `_action_charge_update` | 71 | 80 | 2 | `f2591b2de350c20aefef42c38623875dca494bb46a51d06c6dcd5947b035976e` |
| **Wave gain** | **151** | **160** | **5** | |
The hardened COFF comparator proves identical padded bytes and exact
relocation addresses, types, destinations, and addends. The target owns 6,032
meaningful code bytes in 22 functions and 6,176 padded code bytes. Its fifteen
non-code COMDATs contain 243 payload bytes (256 bytes in the progress metric);
all remain outside this wave's credit. The final candidate owns no runtime
non-code data or storage: it emits no `.rdata`, `.data`, `.bss`, or COMMON
symbol.
## Provenance and ABI
- Baseline `source/ai/action_charge.c` blob:
`4a2578245ccc649a0a6c20823cfb8f204cc201e6`.
- Retained source blob/payload SHA-256:
`d34d932679150399937475d915ade404f7f94ac9` /
`78f7439e940d38166d075c19804f74524e0cb784199c533ab97a8c7b785e9bb4`.
- January executable SHA-256:
`4cc87b45f721270392a96f1674ed2b5cd4a7bb4355faeab4531d1cf1884d9520`.
- January split-object SHA-256:
`76c4c0b62850ffaa42fe5027c3e68b2cc42f7640c1156342c9a9cc4d1125afea`.
- Clean HCEA semantic donor commit:
`c168af2e747d3095d9a29418ae401f3a39544863`. Its
`src/action_charge_begin.c`, `src/action_charge_update.c`, and
`src/action_charge_is_leaping.c` blobs are respectively
`6692a82a1ba231cc7d91c38860096cf6ec80a8e4`,
`3e7d6333d32d89108bfa8b6db76390512094044b`, and
`38fc8c40afa3cc720f5d8f784c22da25150febcc`. Their payload SHA-256 values
are respectively
`9cdc2a06b44a3a69e3418bf79e2aced52111cbd5d9ab9404044c9b472de185c7`,
`a949c7b6fb3ebbd1db455d09f6b0856788d05aa52ce52e0eba3a7b801d1474bd`,
and
`c6857cdfcc0225c97dd8bdfe56a8f5a8ea721881a48ca58e71b8af0911b7a7d5`.
These files supply independently readable behavior, not target-derived
emission anchors.
- Cross-build correspondence commit
`8ffdbab2301d17215b6f4b4a109f0e0a6f09b53d`, blob
`ec76e6464bf79d444779720d5efb9432bf5378d7`, classifies all three names as
exact-unique, ordinary `cdecl`, one-parameter functions. January COFF and
callers remain authoritative for the 32-bit parameter type.
- The existing `struct action_specification` types `begin` and `update` as
`void (*)(long)`. January's action table relocates to the two functions at
`+580` and `+588`. Existing `actions.h` types `action_charge_is_leaping` as
`boolean(long)`, and January `_actor_is_leaping` calls it at relocation
`+33`.
- The earlier evidence-only frontier commit
`677defb9d9b4dd0ac4f20bc2c5dbd53bf3e8d7f5`, ledger blob
`bbb630a9fc673dab443948add44b8457e3b465e4`, independently records all three
public leaves as exact on their first complete readable shapes. It is not a
production-source donor and none of its tuned control/private-helper forms
were reused.
- Compiler: Microsoft 32-bit C/C++ Optimizing Compiler `13.00.9254.1` for
80x86, SHA-256
`483e00c47bb08d699475a642bcff15b5b2036350b31c540e88a506baf101da11`,
with the repository's unchanged XDK 3911 command and flags.
The retained signatures are:
```c
void action_charge_begin(
long actor_index);
void action_charge_update(
long actor_index);
```
Existing `actions.h`, `actors.h`, and `actor_definitions.h` own the action,
actor, charge-state, and firing-variant layouts. The callee's return/argument
types are corroborated by existing source TUs, so the missing declaration is
kept translation-unit-local. The firing-situation enumerator is likewise
source-local because no shared header presently owns it. No shared-header or
storage repair is needed.
## Authenticated behavior and one-shot rejection record
`action_charge_begin` resolves the full 32-bit actor handle through
`actor_get`. For vehicle-strafing charge with the matching firing-variant
special-fire situation, it decrements a positive signed-short deny-attempt
counter. Its target relocations are `DIR32 _actor_data` at `+4`,
`REL32 _datum_get` at `+16`, and
`REL32 _actor_combat_get_firing_variant_definition` at `+37`.
`action_charge_update` resolves the same typed actor handle. During a launched
melee leap, before a melee attack and after the actor is no longer midair, it
increments the signed-short leap-failure timer. Its target relocations are
`DIR32 _actor_data` at `+8` and `REL32 _datum_get` at `+15`.
All three public candidates were compiled together once. The rejected
`_action_charge_is_leaping` target is 66 meaningful/80 padded bytes with three
relocations and normalized SHA-256
`9ddd3d1296e581d078e6958f4039f9bde736698282368436b60d130978e7b275`.
The first natural candidate was also an 80-byte, three-relocation COMDAT, but
its normalized SHA-256 was
`56aa0e8827a4ebecba9e4310f944bc24c07ffb5c2b2e39ef46ea11ad36899f31`.
The target forms one charge-state pointer and shares its epilogue; the readable
HCEA early-return shape instead accesses full actor offsets and duplicates the
return. Consequently the common `game_time_get` relocation occurs at target
`+48` but candidate `+46`. The candidate COMDAT and its support-only prototype
were removed without retry. The one-shot three-body candidate object SHA-256
was `3ee981a618bc3f891786bc839c2c51a3bd11bfb9a16a1e90f382d0569fe3a8af`.
`action_charge_control`, `action_charge_perform`, `action_charge_setup`, both
anonymous `code_` helpers, generic helper emissions, and every data/storage
section were explicitly outside scope and are absent from the final object.
## Policy, scope, and validation
Only `source/ai/action_charge.c` and this new Jonas-owned ledger are changed.
No shared header, storage definition, data section, configuration, semantic
exception, parked record, completion label, protected-five source,
pre-existing Markdown, or Claude-owned file is edited.
The retained source is readable typed C. The guarded decrement remains in the
positive signed-short range. The timer increment is promoted to 32-bit `int`,
so its arithmetic cannot overflow; a hypothetical value above `SHRT_MAX`
narrows according to the implementation-defined two's-complement behavior of
the authenticated VC7/Xbox target. That ordinary target contract is already
used by the project's signed-short AI timers and is not undefined behavior or
an emission control. The source contains no assembly, raw address or
byte-offset dereference, pointer/integer reconstruction, pointer or union pun,
inactive-union access, undefined overflow, volatile scheduling device,
force-inline annotation, optimizer pragma/barrier, synthetic anchor, or
object-byte forcing.
- Forced rebuild of the final retained source: pass. The only defined code
symbols are `_action_charge_begin` and `_action_charge_update`; there is no
runtime `.rdata`, `.data`, `.bss`, or COMMON ownership. Direct hardened
comparison passes both functions.
- Complete `halobetacache_build` and `libcmt_build`: pass (568 remaining edges
after the final translation unit's forced rebuild).
- Semantic audit: 470 units, 4,074 functions evaluated, 3,934 semantic exact,
101 hidden exact / 61,876 hidden code bytes, 3,995 accepted exact, and zero
unit errors.
- Campaign progress: 375/833 complete objects, 3,982/11,060 exact functions,
476,846/2,198,102 meaningful code bytes, and
1,835,088/4,176,062 matched data bytes. This wave adds two functions and 151
meaningful bytes with no data increase.
- Admission audit: zero candidates and zero revocations; only the inherited
`source/shell/shell_xbox` completion-label contradiction remains.
- Parked validation: three active, zero stale, and zero invalid entries.
- Complete tooling suite: 179/179 tests pass; `git diff --check` is clean.
A clean committed-state regression snapshot/check followed by deletion and
forced rebuilding of `action_charge.obj` is the final same-path reproducibility
proof. The post-rebuild hardened comparison must remain strict for both
accepted functions, and the rejected symbol must remain absent. No push is
performed.
@@ -1,120 +0,0 @@
# `action_charge.obj`: setup frame lever and helper-linkage measurement (Opus 5 150K house-clean lane, wave w2, 2026-09-14)
## Result
- `_action_charge_setup` (944 padded, previously a structurally incomplete 688 / 20 residual) is now a complete typed-C body,
landed as **fuzzy** (zero credit): **944 / 29 relocations, instruction-identical to January except the placement of one
`result = FALSE` store** (`mov byte [ebp-1],0` at +0x138 instead of +0xe4).
- A **strict-EXACT** setup candidate exists (`scratch/workers/action_charge/w2/v3.c`, 21/22 object). It is held back because it
needs a January-proven redundant `result = FALSE;` store (see section 4). Owner ruling requested.
- `_action_charge_perform` is unchanged (fuzzy 3216 / 84). The requested helper-linkage measurement is recorded in section 5; the
ruling alone does not produce an exact function.
- Object: `== exact 20 residual 2 unwritten 0` (baseline identical counts). No newly exact function. All 20 baseline-exact rows
keep their listings row for row.
## 1. Baseline and provenance
- Worktree `C:\halo-worktrees\opus5-150k-house-clean-20260914`, base `ea9175577`. Gate without the `_point_from_line3d` guard
(brief 3b: action_charge.obj is the genuine owner; the final object defines exactly one `_point_from_line3d`, strict EXACT,
sha16 `9b763841f8519177`).
- Baseline rows: perform `[size 3216!=3248, sha]`, setup `[size 688!=944, relocs 20!=29, sha]`, 20 EXACT.
- Read: all eight earlier action_charge ledgers, the 100K handoff, the fresh-graphs worker notes and scratch
(`opus5-30k-fresh-graphs-20260914/scratch/workers/action_charge`: `setup_v12.txt`, `t_setup.asm`, `t_perform.asm`).
`branch_sweep`: 12 blobs, none better. A cut-off earlier run in this lane left undocumented probes
(`scratch/workers/action_charge/d_*.c`, `probe/`); used as evidence only.
## 2. New evidence
- **HCEX.pdb (SHIP) locals** (`DIA2Dump -sym`): `action_charge_setup` homes only `short melee_start_tick`,
`short melee_end_tick`, `float melee_start_range`, `float melee_end_range` (all other locals enregistered).
`action_charge_valid_melee_destination` is `static unsigned char (long actor_index, short goal, union real_point3d *melee_target_point)`
with locals `estimated_position`, `collision`. Later build: names and signature shape only.
- **January caller bytes** confirm that signature: setup pushes `[ebp+0xc]` (the short `goal` parameter) and passes
`&prop->center_of_mass` in ESI. The 2026-09-06 ledger's "the point is the second argument, do not import the short
signature" premise is refuted by these bytes; the helper ignores the stack `goal`, and its point parameter is
`melee_target_point`. The helper stays strict EXACT with the authentic signature, and the `(void)melee_target_point;` line is gone.
- `ai_debug.c` charge-decision renderer: `field_198` is the "airborne" (leap) flag, `field_194` is the cannot-move range.
## 3. Setup frame lever (new measured VC7 13.00.9254 law)
January's frame is 0x20 with `leap` (byte) at [-8] and `end_tick` (short) at [-6] in **one** 4-byte cell. The v12 body declares
`leap` and the four tick/range outputs in the same block and gets 0x24 (separate cells). Wrapping the four outputs in a nested
block around the `unit_get_melee_range_and_ticks` if/else (leap stays in the enclosing block) reproduces January's frame and
slot set exactly.
Law: locals declared in the **same** block never share a frame cell. A nested-block local can be packed into, or overlap, an
enclosing local's cell when their live ranges allow it. Probe `scratch/workers/action_charge/probe2/b.c`: f1 (same scope)
frame 20, f2/f3/f4 (outputs nested) frame 16. This contradicts the older note that flattening a block is inert. Only
promotion to the enclosing scope was measured there.
## 4. Setup: the redundant-store blocker
January stores 0 to `result` ([ebp-1]) at +0xe4, which dominates the inhibited test, and again at +0xea (inhibited arm) and
+0x107 (notarget arm). The deep failure arms (no-animation, cannot-move) have no store. Only the literal source
`result = FALSE;` before `if (TEST_FLAG(... inhibited))`, together with `result = FALSE;` in both arms, is strict EXACT
(`w2/setup_exact.txt`).
Non-redundant alternatives measured (all on the nested-scope base):
| Shape | Size / relocs | Outcome |
| --- | --- | --- |
| A1 pre-branch store only | 1024 | tail-duplicated epilogues |
| A4 store at top of melee arm | 1024 | same |
| A6 stores in every failure arm, none before | 1056 | `result` loses its home |
| R1 / R1b / R2 / R3 early `state_data->goal = goal; return ...;` arms | 1024 / 992 / 1024 / 1024 | own epilogues; VC7 does not coalesce return values into `result` |
| **H1** arm stores + `result = FALSE;` first in the melee-attempt arm | **944 / 29** | one store placement differs (**landed, fuzzy**) |
| H3 same store before `prop_get` | 928 | |
| S2a / S5 separate valid/success flag copied into `result` | 1072 / 1072 | |
Owner ruling requested: admit the January-proven redundant store (`w2/v3.c`, the landed file with one `result = FALSE;` moved to
before the inhibited test) for +944 padded exact. The class is the handoff's "January-proven redundant logic"
(`_extract_sequence`).
## 5. Perform: helper-linkage measurement
`w2/v4_inline.c` is the landed file with `__inline` added to the 11 local helper definitions. January's atlas `i` records mark
`normalize2d`, `point_from_line3d`, `magnitude3d`, `normalize3d` and `collision_test_line` as inline COMDATs.
- All 20 other rows stay strict EXACT, with no new owner.
- Perform goes from 3216 to 3264 padded (meaningful about 3250 against January's 3246).
- The two separate `direction = prop->actor_to_prop` copies come back.
What remains after the lever:
- one extra `mov ecx,[ebp-8]` prop reload at +0x256 (the only instruction-count difference);
- the melee-abort block: January keeps `check_range` in BL and reloads the prop, with cells `check_range` [-2] and
`berserk_ranges`+`melee` [-1]; ours keeps the prop in EBX;
- the `&field_114` register;
- x87 scheduling of `lead_fraction * ticks`, the suicide dot product and the ticks `movsx`;
- argument registers of three calls.
Two more shapes were inert: W1 (the weapon pointer in its own block) and W3 (negated-or LOS test). Not landed:
perform is an already-written residual and none of these is strict EXACT.
## 6. Checks (landed real file)
- `gate --all`: `exact 20 residual 2 unwritten 0`. The diff against HEAD's listing is only the setup row
(`[size 688!=944, relocs 20!=29, sha]` becomes `[sha]`).
- Parks: none for the unit.
- Owner census against `build/base`: the only new owners are the target-owned `actor %s melee animation has no damage keyframe`
string and the `state_data` assert literal, which January references as undefined. Pre-existing non-target owners
(`_vector_from_points3d` SELECT_ANY, `__real@41f00000`, `__real@3f000000`, D3D tables) are unchanged.
- `tools/fake_match_scan.py`: 0 leads.
## 7. Proposals
1. Owner ruling: admit `w2/v3.c` (redundant `result = FALSE;` proven by January +0xe4/+0xea/+0x107) as strict EXACT setup.
2. Helper linkage (`__inline` local helper definitions): measured in section 5. It does not give exact credit on its own, so it is
not needed now.
3. Pre-existing debt, not introduced here: action_charge.c carries a consumer-local prototype of units.c's
`unit_get_melee_range_and_ticks`. Its owner would be `source/units/units.h`, which has 66 includers
(declaration-count risk, units.c protected neighbour). Proposed text, placed next to `unit_melee_attack_begin`:
`boolean unit_get_melee_range_and_ticks(long unit_index, boolean secondary, short *melee_tick, real *attack_time, short *frame_count, real *damage_time);`
in house one-parameter-per-line form.
## 8. Reopen criteria
- **setup:** the owner ruling above, or a non-redundant source that emits a dominating FALSE store before the inhibited branch
while keeping both arm stores.
- **perform:** a helper-linkage ruling plus a source-level cause for the +0x256 prop reload and the `check_range` BL allocation.
Do not re-permute declaration order or scopes (fresh-graphs do-not-repeat list).
@@ -1,188 +0,0 @@
# `action_charge.obj`: setup store-position closure proof and perform linkage re-measurement (Opus 5 250K house-clean lane, wave w3c, 2026-09-15)
## Result
- **Nothing landed.** `git diff -- source/ai/action_charge.c` is empty; the object is unchanged at
`== exact 20 residual 2 unwritten 0`, identical row for row to the baseline listing.
- `_action_charge_setup` (944): re-measured at **291 instructions vs January's 291 with exactly ONE aligned
difference** — the position of `mov byte ptr [ebp-1],0` (January `+0xe4`, ours `+0x138`). This wave adds a
**construction proof that no non-redundant source spelling can place that store at `+0xe4`**, so the row is
definitively owner-blocked on the laws_w3 A42 / R12 redundant-store ruling, not on a missing shape.
- `_action_charge_perform` (3248): the `[size 3216!=3248]` gap is **fully attributed** to a single cross-jump —
January emits the `direction = prop->actor_to_prop;` copy twice, ours once. The linkage lever was reproduced and
a strictly better-motivated route for it was found (`p2.c`: delete the per-TU rename package and the 11 local
helper bodies, take the helpers from `math/real_math.h`), producing **byte-identical** output to the previously
declined `__inline` form. Neither reaches exact (3264), so neither is landable; the residual is 59 real
allocation blocks.
## 1. Baseline and provenance
- Worktree `C:\halo-worktrees\opus5-150k-house-clean-20260914`, branch `opus/250k-house-clean-20260915`.
- Gate WITHOUT the `_point_from_line3d` guard (brief 3b exception: this object is the genuine owner). The guard run
fails only on that pre-existing owner: one code definition, 48 bytes, strict EXACT, sha16 `9b763841f8519177`.
No additional candidate-only math owner exists (census below).
- Baseline rows: `residual 3248 _action_charge_perform [size 3216!=3248, sha]`, `residual 944 _action_charge_setup [sha]`,
20 EXACT. No parks for the unit.
- Read in full: all nine action_charge ledgers (2026-08-21 two-leaf, the three 2026-08-26 math-helper ledgers,
2026-08-28 public residuals, 2026-08-29 helper reintegration, 2026-09-06 authentic math header owners,
2026-09-14 fresh-graphs, 2026-09-14 150K w2), `scratch/workers/action_charge.md`, the 100K handoff,
`scratch/w2/laws_w2.md` and `scratch/w3/laws_w3.md` in full.
- `git log --all --oneline -- source/ai/action_charge.c`: 16 refs. `branch_sweep`: 13 unique blobs, HEAD is the best
(`exact 20 residual 2 unwritten 0 sizedelta 32`); nothing to reuse.
- Scratch: `scratch/workers/w3c_action_charge/` — `base.c/.obj`, `p1.c/.obj`, `p2.c/.obj`, `setup_aln.txt`,
`perform_aln.txt`, `perform_aln_full.txt`, `p1_aln.txt`. Worker notes: `scratch/workers/w3c_action_charge.md`.
## 2. `_action_charge_setup` — the shape space is closed
`alndiff` against the HEAD build (`setup_aln.txt`):
```
target 291 instructions, ours 291 instructions
--- delete target[67:68] ours[67:67] @T 0xe4 T e4 mov byte ptr [ebp - 1], 0
--- insert target[88:88] ours[87:88] @O 0x138 O 138 mov byte ptr [ebp - 1], 0
```
(the three other reported blocks are `__FILE__`/`.rdata` label artifacts of `--source`, not code differences).
January's `[ebp-1]` writes: `+0x45 = 1` (the sunk `boolean result = TRUE;` initializer — no entry store, so A42's
"VC7 does not hoist a FALSE into the entry" detector says `+0xe4` is a *separate* source statement), `+0xbc = 0`
(swarm arm), `+0xe4 = 0`, `+0xea = 0` (inhibited arm), `+0x107 = 0` (notarget arm), `+0x308 = 1` (melee success),
`+0x383 = al` (vehicle arm), read once at `+0x39a`.
Context of the divergence:
```
d9 mov cl, byte ptr [eax + 0xb6]
df add esp, 8
e2 test cl, cl
e4 mov byte ptr [ebp - 1], 0 <-- between the compare and the jcc
e8 jns 0xfc
ea mov byte ptr [ebp - 1], 0
ee mov word ptr [edi + 0x190], 3
```
**A38** (initializer / zero-store placement) fires exactly: a store emitted between an `if`'s compare and its `jcc`
belongs to the block *enclosing* that `if`, not to its body — i.e. the source statement sits before
`if (TEST_FLAG(unit->object.damage_flags, _object_melee_attack_inhibited_bit))`.
**Closure proof (new).** Both bodies emit four zero stores to `result`. In any source where every path assigns
`result` exactly once, the fourth store is the default for the deep melee-attempt path, and its only non-redundant
position is inside that arm — which is the landed H1 shape and emits at `+0x138`. Conversely a store emitted at
`+0xe4` necessarily precedes the inhibited test, where it dominates the inhibited and notarget arms; those arms'
own stores cannot be dropped (w2 measured A1 "pre-store only" at 1024 and A6 "arm stores only" at 1056 — removing
them re-creates nine tail-duplicated epilogues). Hence **strict EXACT requires a store that is redundant with
respect to two arms**, and the eleven alternatives already measured in w2 (A1, A4, A6, R1, R1b, R2, R3, H1, H3,
S2a, S5) are exhaustive of the non-redundant space. No shape was spent this wave; the proof replaces the probe.
The strict-EXACT body already exists: `scratch/workers/action_charge/w2/v3.c` (21/22 object) = the landed file with
`result = FALSE;` moved one source line, from the top of the melee-attempt arm to immediately before the inhibited
`if`. Class: laws_w3 C3 "Byte-proven redundant stores/branches (A42)", alongside `_encounter_test_rule` (tr8) and
`_extract_sequence`.
**Owner ruling requested (re-stated, with the new closure proof): +944 padded strict-exact bytes for one moved
source line, zero sibling and zero park risk.**
## 3. `_action_charge_perform` — the `[size]` gap attributed, and the linkage route improved
Production: 3216 / 84, **983 instructions vs January's 982**, relocation multiset identical.
### 3.1 The 32-byte gap is one cross-jump
January emits the `direction = prop->actor_to_prop;` struct copy twice:
```
6c8 mov eax,[ebp-8] <- then-arm of `if (dot_product3d(&direction,&prop->actor_to_prop) < 0.f)`
6cb fld dword ptr [__real@00000000] (distance = 0.f, interleaved)
6d1 mov ecx,[eax+0xe0] / 6d7 mov edx,[eax+0xe4] / 6dd mov eax,[eax+0xe8]
6e3 mov [ebp-0x2c],ecx / 6e6 mov [ebp-0x28],edx / 6e9 mov [ebp-0x24],eax
...
707 mov eax,[ebp-8] ... (second copy, different register schedule) <- inside `if (distance == 0.f)`
```
Ours emits it once at `+0x6e7`: the inlined `dot_product3d(&direction, &prop->actor_to_prop)` strength-reduces the
argument address to `add edx,0xe0` at `+0x6a1`; both copies then become the identical `mov ecx,[edx] …` sequence
and VC7 cross-jumps them. The eight-instruction target block against our one-instruction stub *is* the
`3216 != 3248` delta. The same biased pointer also reorders the inlined dot: January k,j,i (A35(2)
youngest-term-first), ours j,k,i.
**Detector caveat for A35 (new):** a k,j,i vs j,k,i term order can be an addressing-mode artifact of a
strength-reduced `&struct->field` pointer rather than a source expression-order or leaf-age fact. Look for an
`add reg,imm` bias in the candidate before spending an A35 shape on term order.
### 3.2 The linkage lever, reproduced and improved
| Shape | Size / relocs | perform sha16 | Siblings | Owner census | Landed |
| --- | --- | --- | --- | --- | --- |
| HEAD (rename package + 11 local helper definitions) | 3216 / 84 | `2e9beea61ca0c704` | 20 EXACT | unchanged | (in tree) |
| `p1.c` = HEAD + `__inline` on the 11 local definitions (w2 `v4_inline`) | 3264 / 84 | `798a99326c8aff1a` | 20 EXACT | unchanged | no |
| `p2.c` = HEAD with the rename package **and** the 11 local definitions **deleted** (helpers come from `math/real_math.h`) | 3264 / 84 | `798a99326c8aff1a` | 20 EXACT | `+_magnitude_squared3d` (code) | no |
`p1` and `p2` produce **byte-identical** perform sections, so inline *linkage* is the only operative variable; the
local `__inline` copies are not needed to obtain it. `p2` is the better-motivated form: January's map atlas marks
`normalize2d`, `point_from_line3d`, `magnitude3d`, `normalize3d` and `collision_test_line` as inline (`i`) COMDAT
copies, and `p2` removes an A30/R8-class per-TU `#define helper helper_inline` rename package plus 114 lines of
duplicated real_math bodies from a consumer `.c`. Its only cost is one surplus candidate-only COMDAT
`_magnitude_squared3d`, the same real_math.h select-any class as this object's already-disclosed
`_vector_from_points3d`.
Neither reaches 3248, so under brief section 6 (already-written residual: land only if strict EXACT) neither is
landable, and the fresh-graphs lane's decline of the `__inline` route stands. Recorded so a later wave does not
re-litigate the linkage question.
### 3.3 Residual after the lever (59 real blocks, `p1_aln.txt`)
109 aligned blocks, 59 after removing relocation-annotation and branch-offset noise (HEAD base: 67). All are
allocation:
- extra `mov ecx,[ebp-8]` prop reload at `+0x256` (+3 bytes; the only instruction-count difference);
- melee-abort byte pair swapped — January `check_range` at `[ebp-2]`, `berserk_ranges` (and the later `melee`) at
`[ebp-1]`; January also keeps `check_range` in BL across `game_time_get` where ours keeps `prop` in EBX and
reloads the flag (`mov al,[ebp-1]; test al,al` at `+0x387`, +5 bytes);
- `_charge_goal_melee` rematerialised as an immediate by January (`cmp word [edi+4],2`, `mov word [edi+4],2`) vs
pinned in ECX by us (`mov ecx,2` … `cmp word [edi+4],cx`) — the constant-register pinning family, inverted;
- `[ebp-0x14]` vs `[ebp-0x18]` for the tail `unreachable` byte;
- two commutative `fadd` operand orders at `+0x944` / `+0x956` — R3 forbids swapping the source operands for these;
- cdecl-pop (`add esp,N`) placement at `+0x6a2`, `+0x8e7`, `+0x919` (A31(a));
- roughly 35 pure eax/ecx/edx register-name substitutions.
Net 3252 meaningful vs January's 3246.
## 4. Shapes spent and rejected
- setup: 0 this wave (closure proof; the 11 w2 alternatives are exhaustive of the non-redundant space).
- perform: 2 of 5 (`p1`, `p2`), neither exact, neither landed.
- Designed and rejected on paper: setup S1 (split the inhibited else-if into `if`/`else` with one default in the
else) — yields three zero stores, not January's four, so it cannot be exact.
- Not re-spent (prior do-not-repeat): declaration order / block scope / names / dead initialisers of `leap` and the
tick outputs; early-return arms; `check_range` / `berserk_ranges` / `abort_range` / `time` / `target_range` /
`unreachable` declaration order and scope; dot_product3d argument order; `danger + attack` order; moving perform
before the helper package.
## 5. Checks
- Final real-file gate (no guard): `== exact 20 residual 2 unwritten 0`, listing identical to baseline row for row.
- Guard gate: fails only on the pre-existing genuine `_point_from_line3d` owner (one definition, 48 bytes, EXACT).
- Parks: `scratch/parkcheck.py` → "no parks for source/ai/action_charge".
- Owner census vs `build/base`: HEAD `candidate-only vs base: []`; `p1` `[]`; `p2` `[('_magnitude_squared3d','code')]`.
Pre-existing non-target owners (`_vector_from_points3d`, `__real@3f000000`, `__real@41f00000`, the two assert
literals, the D3D tables) unchanged in all three.
- `tools/fake_match_scan.py source/ai/action_charge.c`: 0 review leads.
- `git diff --stat -- source/ai/action_charge.c`: empty.
## 6. Proposals
1. **Owner ruling (setup, A42/R12 class):** admit `scratch/workers/action_charge/w2/v3.c`. One moved source line
(`result = FALSE;` before the inhibited `if`, arms unchanged) for +944 padded strict-exact bytes. This wave adds
the proof that no non-redundant spelling can reach exact, so the ruling is the only remaining path for this row.
2. **Census/policy note (perform):** if a later wave closes the allocator items, the linkage prerequisite should be
taken via `p2.c` (delete the rename package and the local helper bodies) rather than the declined local
`__inline` form; the orchestrator would then need to accept `_magnitude_squared3d` as a real_math.h surplus
select-any disclosure, the same class as this object's existing `_vector_from_points3d`.
## 7. Reopen criteria
- **setup:** the owner ruling in section 6.1. Do not re-spend on store placement, declaration order, block scope or
early-return shapes.
- **perform:** a source-level cause, measured on the `p2` base, for (a) the `[ebp-1]`/`[ebp-2]` byte-pair rank in the
melee-abort block that also puts `check_range` in BL, and (b) the `+0x256` prop reload. Do not re-spend on
declaration order/scope of the abort-block locals, dot_product3d argument order, or commutative `fadd` swaps (R3).
@@ -1,148 +0,0 @@
# `action_charge.obj`: perform reconstruction and setup frame residual (Opus 5 fresh-graphs lane, 2026-09-14)
## Result
- `_action_charge_perform` (3,248 padded, previously UNWRITTEN) is now a complete typed-C body, landed as
**fuzzy** (exact credit 0): 3,216 / 84 relocations vs 3,248 / 84, identical relocation destination multiset.
- `_action_charge_setup` (944-byte residual) was reconstructed to **944 / 29 with identical instruction opcodes**;
only frame-slot displacements differ. It is **not landed** (not strict exact).
- All 20 baseline-exact functions stay strict exact with unchanged normalized hashes.
- Object: `== exact 20 residual 2 unwritten 0 (of 22 listed)` (baseline `exact 20 residual 1 unwritten 1`).
No newly exact function.
## 1. Scope and provenance
- Unit `source/ai/action_charge.c`, target `build/split/source/ai/action_charge.obj`, lane base `47d9dd0da`.
- XDK 3911 `CL 13.00.9254` with `/O2 /Oy- /DDEBUG /Dxbox` (unchanged; `/O2` auto-inlines, see section 6).
- Evidence used: January COFF disassembly (Capstone, relocation-annotated), Ghidra 12.1.2 decompilation
`scratch/ghidra/out/action_charge.obj.decomp.c` (perform 383-832, setup 891-1039), January map atlas
`scratch/atlas/action_charge.obj.txt`, HCEA `src/blam/ai/action_charge_perform.c` / `action_charge_setup.c`
(later build, semantics only), `source/ai/ai_debug.c` charge-decision and melee-check renderers (field meanings),
`source/ai/actor_firing_position.c` weapon minimum-range idiom, `source/ai/action_fight.c`
`actor_perception_unreachable` prototype.
- Prior work read in full: the seven existing action_charge ledgers and the archived frontier ledger
`677defb9d:docs/object_matching_logs/action_charge_obj.md` (its perform/setup bodies were never committed;
branch_sweep over 11 blobs found no body in any lane).
## 2. Validated baseline
| Function | Target padded | Relocs | Baseline | Now |
| --- | ---: | ---: | --- | --- |
| `_action_charge_perform` | 3,248 | 84 | UNWRITTEN | fuzzy 3,216 / 84, sha16 `2e9beea61ca0c704` (target `9ed9d44b1e9fd722`); first divergence +0x256 |
| `_action_charge_setup` | 944 | 29 | 688 / 20 | unchanged in production; best candidate 944 / 29 sha16 `31040fe5ab789ce3` (target `34c0c115890ecab8`); first divergence +0x3 (`sub esp,0x24` vs `0x20`) |
| 20 others | 1,984 | | EXACT | EXACT, hashes unchanged |
Data: production now also owns the target literals `__real@3f5db3d7`, `__real@3cbf258c`, `__real@3e99999a`,
`__real@3d4ccccd`, `__real@3f4ccccd` (perform). `__real@41f00000` and the `!actor->meta.swarm` string are
emitted for references January resolves as undefined externals. The setup-only
`actor %s melee animation has no damage keyframe` string stays absent until setup lands.
## 3. Accepted controls (production)
`action_charge_perform`, placed between the helper package and the private destination helper:
- declarations in January call order: actor, actor definition (`actr`), variant definition (`actv`), firing variant,
charge state, `prop = NULL`, `result = FALSE` (January keeps `[ebp-3]` and returns it on the default path);
- prop-valid branch first with the "no target" arm last (`je` to +0x43c); a separate delayed-projectile arm
(January lays `advancing = TRUE` right after that test and cross-jumps the vehicle-goal arm into it);
- stalking exposure assigned to `state_data->stalking_currently_exposed` and re-read (January materialises it at
32-bit width, which a boolean local does not reproduce);
- close-range bounds with the `actor_firing_position.c` `MAX(minimum_range, weapon->weapon.ai_minimum_target_range)`
idiom (weapon +0x40c);
- melee abort: `MIN(abort_range, maximum_abort_range)` with a named `maximum_abort_range = MAX(0.f, melee_attack_range) + 0.8f`
(January stores it to a slot and compares with `fcom`), asserts at lines 288 and 487;
- melee check debug telemetry into `actor_debug_array` (`last_melee_time`, `field_108/114/120/12C/138/139/13C/148/14C`)
exactly as `ai_debug.c` renders it;
- `dot < 0` if/else distance form (two separate `direction = prop->actor_to_prop` copies in January),
leap arm `distance < lower && unreachable_ticks == 0 && !flying` aborts, ballistic leap with January's argument
mapping (13th = vertical, 14th = horizontal);
- communication immediates as enums: `_ai_communication_melee` (43), `_comm_hostility_enemy` (3);
- tail: `target_prop` re-fetch with `out_of_range = FALSE; if (...) out_of_range = TRUE;` (January `xor cl,cl` form),
single `return result;`.
Supporting source-local additions: includes `ai.h`, `ai_communication.h`, `items/projectiles.h`,
`items/weapon_definitions.h`, `ai_debug.h`; enum constants `_action_charge_prop_facing_central = 2`,
`_action_charge_prop_closing_speed_slow = 1`; TU-local `actor_perception_unreachable` prototype (same as action_fight.c).
The `(real_vector2d *)` casts on 3D vectors follow `actors.c` and January's call operands (the 3D aim vector's address
is passed to `normalize2d`).
## 4. Experiment matrix
Scores are differing instructions after alignment (`scratch/workers/action_charge/score.py`, relocation offsets normalised).
### perform
| Shape | Size / relocs | Diff insns | Kept |
| --- | --- | ---: | --- |
| first complete body | 3280 / 87 | - | no |
| prop-valid first, delayed arm split, exposure via state | 3248 / 87 | - | yes |
| named maximum_abort_range | 3216 / 84 | 144 | yes |
| dot<0 if/else, leap-abort polarity, order fixes (package helpers) | 3216 / 84 | 202 | yes (authentic form; package merges copies) |
| same, helpers marked `__inline` locally (not landed) | 3264 / 84 | 142 | lever only |
| check_range/berserk declaration orders (3 variants) | = | 140-141 | inert |
| target_range/time scopes (3 variants), unreachable scope | = | 140-141 | inert |
| `field_114` component copy | = | 135 but loses January's pointer copy | no |
| `target_prop` + if-form out_of_range, declaration order | = | 127 (`__inline` mode) / 182 (production) | yes |
| abort ternary as if/else, compare operand swap | = | 127 | inert |
| dot_product3d argument swaps, danger/attack sum order | = | 142 | inert (VC7 canonicalises) |
### setup (best candidate `scratch/workers/action_charge/setup_v12.txt`, not landed)
| Shape | Size / relocs | Notes |
| --- | --- | --- |
| single exit + debug decisions + assert + csmemset + keyframe error | 1040 / 29 | 9 tail-duplicated epilogues |
| + `result = FALSE` in inhibited/notarget arms (January stores at +0xbc/+0xe4/+0xea/+0x107) | 1040 / 29 | removing them later re-creates the duplication |
| + `move_range = MAX(minimum_move_range, target_range)` | 928 / 29 | duplication gone |
| + `state_data->leap_possible_if_at_range = real_random() < chance; leap = ...` | **944 / 29** | 32-bit compare width as January; only frame displacements differ |
| frame diagnostics: 16 declaration-order / scope / name / init / `valid` / variant-local variants, `/Fa` listings | 944 / 29 | `leap` and `end_tick` never share January's 4-byte cell |
## 5. Do-not-repeat
- perform: declaration order and block scope of `check_range`, `berserk_ranges`, `abort_range`, `time`,
`target_range`, `unreachable`; dot_product3d argument order; `danger + attack` operand order; moving perform before
the helper definitions (VC7 compiles bottom-up; identical output).
- setup: declaration order, block scope, names, dead initialisers of `leap` / tick outputs; removing `valid`; variant
pointer vs name local; `leap = cmp ? TRUE : FALSE` / `(long)` casts (keep 8-bit width).
- Pure header helpers (removing the rename package) emit candidate-only `_magnitude_squared3d` (LNK2005 history in
`action_charge_obj_jonas_math_helper_recovery_rejection_20260826.md`); do not land that.
## 6. Residual classification
- perform (measured): relocation multiset identical; remaining differences are register choices (melee-abort block
`check_range` in BL vs `prop` in EBX; `field_114` and `field_12C` copy registers; argument registers of
`unit_melee_attack_begin` / `ai_communication_event`), byte-cell packing of `berserk_ranges` / `check_range`, and
x87 operand order of three inlined dot products. **Inference, with a measured lever:** the inherited
NODUPLICATES helper package lets VC7 use normalize3d/normalize2d clobber sets (EDX kept live across the call);
January's helpers were SELECT_ANY inline COMDATs (atlas `i` records), so its caller could not. Marking the local
helper definitions `__inline` keeps all 11 helpers strict exact with no new owner and cuts perform's residual from
182 to 127 instructions. Not landed because it changes exact siblings' linkage.
- setup (measured): instruction stream identical; residual is frame layout only (January frame 0x20 with `leap` at
-8 and `end_tick` at -6 in one cell; candidate 0x24). No source lever found.
## 7. Reopen criteria
- Orchestrator decision on helper linkage (local `__inline` package, or header-owned helpers with January's
`magnitude3d` body that does not reference `magnitude_squared3d`), then continue perform from
`scratch/workers/action_charge/pbase3.txt` on that base.
- For setup: evidence of the original tick-output locals/types that makes `leap` and `end_tick` non-interfering for
VC7's frame packer; land `setup_v12.txt` together with the destination-helper signature
`(long actor_index, short goal, real_point3d const *melee_target_point)` only when strict exact.
## 8. Disposition
Object remains NonMatching: 20 strict exact, `_action_charge_perform` fuzzy (credit 0), `_action_charge_setup`
residual. No config, header, park, flag or comparator change.
## Lead admission notes (2026-09-14)
- The worker's TU-local `actor_perception_unreachable` prototype was replaced
by the owning declaration added to `source/ai/actor_perception.h` in the
separate prerequisite commit `a694cf1b2` (which also removed action_fight.c's
consumer-local copy). `action_charge.c` now includes that header.
- The leap stores were reordered to the natural `leap_pending = TRUE` first,
as measured by the adversarial reviewer; the emitted body is byte-identical
(3216/84, sha16 `2e9beea61ca0c704`).
- The proposal to mark the 11 local math helpers `__inline` was NOT adopted:
it changes COMDAT selection of 11 already-exact sections without an exact
gain and needs its own whole-board review. `_action_charge_perform` remains a
zero-credit fuzzy body; `_action_charge_setup` stays at its baseline residual.
@@ -1,129 +0,0 @@
# `action_converse.obj`: ordinary-C restoration and fuzzy park
## Outcome
`source/ai/action_converse.c` now contains a credible ordinary-C
reconstruction of `action_converse_perform`. The former implementation used
three inline-assembly regions to transcribe January's shared-`TRUE` register
choice. That was an exact machine-code reproduction, not credible original
gameplay source, and is not allowed by the rule limiting sparse inline
assembly to helper and math functions.
The ordinary-C function is intentionally retained at the measured compiler
boundary:
- object-local strict result: `8 / 9` functions, `640 / 896` padded bytes;
- target packet: `256` padded bytes, `8` relocations,
normalized SHA-256
`7a3fad8fb69307c132558b906832d4d31f0334734fc76d18fe773189b5a38d5d`;
- base packet: `256` padded bytes, `8` relocations,
normalized SHA-256
`288862a662942eb5b03ee84809dbd6b6aea1c023d52915f2bc373192e7c212cb`;
- objdiff fuzzy score: `93.14815%`;
- park class: `register-allocation`.
No exact-match credit is claimed for this function.
## Source credibility and semantics
The retained topology is the clean donor preserved at
`C:\halo-worktrees\opus-dual-scheduler-research\dispo-wt`, commit
`5ac58ab8731e9227de6fd87afdf0542ce318aa59`. The same body originated in
commit `b4651a63db29932d5a6640d0c7b5af59a13c8463`.
The function performs a coherent sequence:
1. Resolve the conversation's unit to a prop when no prop is cached.
2. Mark the actor in range when the prop is fully perceived and inside the
requested distance, or is closer than `0.7` world units.
3. Halt movement once in range.
4. Otherwise request movement toward the prop and record failure when that
request fails.
5. Record failure when no prop can be resolved.
This is not an accidental or nonsensical byte match. The state transitions,
callee contracts, field offsets, and relocation identities agree with the
January function. HCEA interface evidence at commit
`570c83fd9c365dad6f2a3e7041705d5b84c7847c` supports the action-table return
contract and the reconstructed behavior. The open Marathon source was
checked as a naming/style reference, but it supplies no stronger homolog for
this Halo-specific conversation action.
The numeric visibility threshold was replaced with the authentic enum
constant `_actor_perception_full`. January accesses `prop->visibility`; an
HCEA reconstruction using `team_index` at that point is inconsistent with the
January offset and was not copied.
The local `true_value` declaration and assignment remain separate even though
the general style preference is to combine them. This is the strongest
measured natural-C packet, and moving its initialization is one of the already
exhausted lifetime shapes.
## Residual compiler boundary
The target and base have the same eight relocation identities in the same
order:
| Relocation | Target | Base |
| --- | ---: | ---: |
| `_actor_data` | `+4` | `+4` |
| `_datum_get` | `+17` | `+16` |
| `_prop_get_base_by_unit_index` | `+67` | `+63` |
| `_prop_data` | `+107` | `+103` |
| `_datum_get` | `+114` | `+110` |
| `__real@3f333333` (`0.7f`) | `+157` | `+153` |
| `_actor_move_halt` | `+186` | `+183` |
| `_actor_move_to_prop` | `+220` | `+216` |
January retains `TRUE` in `EBX` and uses `BL` for the two byte stores. The
lawful VC7 C output folds those values into immediate operands. Five grounded
source shapes were exhausted without finding a credible C lever. The former
asm forced the target register allocation directly and was therefore removed
rather than treated as a reconstruction.
## Prototype ownership cleanup
The same wave removes use-site declarations and places the contracts with the
closest associated headers:
- `prop_get_base_by_unit_index` is declared under `prototypes/PROPS.C` in
`source/ai/props.h`;
- `actor_move_to_prop` and `actor_move_halt` are declared under
`prototypes/ACTOR_MOVING.C` in `source/ai/actors.h`, because
`actor_moving.c` has no dedicated header;
- `actor_move_halt` uses its authentic `boolean` return contract rather than
the inaccurate local `void` declarations. Existing callers ignore the
value, so their call behavior is unchanged;
- duplicate declarations were removed from `actions.c`, `action_charge.c`,
`action_converse.c`, `action_fight.c`, `action_guard.c`, `action_search.c`,
`action_wait.c`, and `ai_communication.c`.
All project real-valued parameters use `real` from `cseries.h`; no raw
`float` interface was introduced.
## Verification
- Focused VC7 compile of `build/base/source/ai/action_converse.obj`: pass.
- Full configured build, semantic report, admission audit, and progress gate:
pass with `0` unit errors.
- `tools.coff_compare` confirms the packet sizes, hashes, and relocation
identities above.
- The pre-change regression manifest reports exactly one intentional change:
the formerly accepted asm transcription is now nonexact. All eight
inherited strict-exact Action Converse functions remain exact.
- Park validation: `13` active, `0` stale, `0` invalid.
- Strict campaign board after the cleanup: `278 / 619` fully exact objects,
`4,857 / 8,246` strict-exact functions, and
`674,823 / 1,922,669` padded bytes. The perform function is counted as
parked, not exact.
- Test suite: `212 passed`.
- `git diff --check`: pass.
- The rebuilt object has no `point_from_line3d` symbol or COMDAT; geometry and
the January inline schedule are untouched.
Raw object hashes for the frozen verification build:
- target `action_converse.obj`:
`9a69bb69f5590a576fd82f635f47fdcb037b6ce5410d5ce2e5b4d3bd92510a35`;
- base `action_converse.obj`:
`e12f04462cff30aa8d59e15ff5d64a786ebc5d19506ed5147103a34848dc731b`.
@@ -1,126 +0,0 @@
# `action_flee.obj` Jonas two-leaf wave
## Result
This bounded source-only wave starts from authoritative integration commit
`a03585774f859697a788a73e4c3fa6bde7c6e3ea`. Six HCEA-backed action-flee
bodies were emitted together in one natural candidate compile. Two were
independently strict and are retained. The four nonexact bodies and their
support-only declaration were removed immediately, without a spelling,
declaration, control-flow, or compiler-control retry. `action_flee.obj`
advances from 0/13 to 2/13 exact functions and remains `NonMatching`.
| Retained function | Meaningful | Padded | Relocations | Normalized SHA-256 |
| --- | ---: | ---: | ---: | --- |
| `_action_flee_end` | 41 | 48 | 3 | `649eb65fd4e8e516d5c8675b4dbf5e08dab7b6d07888e25af8327f32b5c6be98` |
| `_action_flee_blind_panic` | 30 | 32 | 0 | `7bcf33bcce1177b460ded4cc5dee5518afa9db14695e8fdd49652483675b6297` |
| **Wave gain** | **71** | **80** | **3** | |
The hardened COFF comparator proves identical padded bytes and exact
relocation addresses, types, destinations, and addends for both retained
functions. The object now reports 71/3,312 meaningful code bytes and two
exact functions. The target's remaining eleven functions and all 312
non-code bytes stay outside this wave's credit.
## Provenance and signatures
- Baseline `source/ai/action_flee.c` blob:
`73990e6dee5af609201a2b17af56424f3276161a`.
- Retained source blob/SHA-256:
`65b90ce0ba18f6e01c13ef33c8b6bd539db16079` /
`67e862acfb2a040c4a652ecbe85c842d1a9869a3159eefc9bc3059bb351874b7`.
- January executable SHA-256:
`4cc87b45f721270392a96f1674ed2b5cd4a7bb4355faeab4531d1cf1884d9520`.
- January split-object SHA-256:
`5d6e667103c4b42824a5e1c69fb047860a60d901eda36153162cf19672eff59e`.
- Clean HCEA semantic donor commit:
`c168af2e747d3095d9a29418ae401f3a39544863`. Its exact-name
`action_flee_end.c`, `action_flee_flush_position_indices.c`,
`action_flee_modify_color.c`, `action_flee_replace_prop.c`,
`action_flee_blind_panic.c`, and `action_flee_begin.c` files supply the
six natural typed behaviors.
- January PC disassembly independently fixes 32-bit actor datum handles,
the `actor_get`/`datum_get` path, the named flee-state offsets, the signed
panic interval `[9, 12]`, and both unit blind-running calls. January COFF
remains authoritative for symbol identity, bytes, padding, and relocation
ownership.
- Compiler: Microsoft 32-bit C/C++ Optimizing Compiler `13.00.9254.1` for
80x86, with the repository's unchanged XDK 3911 command and flags.
The retained signatures are:
```c
void action_flee_end(
long actor_index);
boolean action_flee_blind_panic(
short panic_type);
```
The existing `actors.h` owns the typed actor/action layout and `actor_get`
contract. The existing `units.h` declaration owns the stop-running call.
No shared-header repair is needed.
## Authenticated behavior and one-shot rejection record
`action_flee_end` resolves the complete 32-bit actor handle through
`actor_get`; when the actor owns a valid unit datum, it calls the typed
`unit_stop_running_blindly` API. `action_flee_blind_panic` returns true only
for the inclusive signed-short interval from grenade attachment through
burning to death, represented by the source-local enum values 9 and 12.
Both bodies have explicit terminal returns.
All six candidates were compiled together once. The following four misses
were removed without retry:
| Rejected function | Target meaningful / padded / relocs | Target hash | First natural candidate |
| --- | ---: | --- | --- |
| `_action_flee_flush_position_indices` | `39 / 48 / 2` | `8dad23011290d89f5753258c4f1a2c41b21837956d5556a5de2c9a344de11329` | `48 / 2`, SHA `8da380385d9a8cfbb0f1465c1bef5c314add784ef72b616032e8646cded81413`; same relocation identities, different normalized instruction schedule. |
| `_action_flee_modify_color` | `99 / 112 / 4` | `9eef0cd0460952e50234aa38cad6a232cef2ed909f574428956b38026790dfb2` | `80 / 4`, SHA `61e086d8f401f02517b7ad56f1938c82a0121a05b697fba3cc11b86747d8685d`; the optimizer joined the color-copy tails instead of January's duplicated branches. |
| `_action_flee_replace_prop` | `48 / 48 / 2` | `10b5ae0e39ea2d834df55c0984c57bad0465ad8ee69612fc6cc02a1f7762cbf0` | `48 / 2`, SHA `48b2a3f2fe62a1474a20508f3c90d3866f70e390310adee035f55fd0db12bbf4`; same relocation identities, different normalized instruction schedule. |
| `_action_flee_begin` | `92 / 96 / 3` | `009e97240ce60b10098e18f6f625c4b678cd424dd832d92d69e7a3eb485ea9a0` | `96 / 3`, SHA `1aa8574ec4ab3fee338e6ea51c0ad4be4b1e0a7b2098b18442f21131151c2fab`; the unit-start call relocation lands at `+79` instead of January's `+83`. |
All four rejected COMDATs and the begin-only unit-start declaration are
absent from the final object. Reopen them only with independently preserved
January source or a newly authenticated ordinary-C contract; do not tune
their spelling or scheduling from this rejected object.
## Policy, scope, and validation
Only `source/ai/action_flee.c` and this new Jonas-owned ledger are changed.
No shared header, storage definition, data section, configuration, frozen-five
source, semantic exception, parked record, pre-existing Markdown, or
Claude-owned file is edited.
The retained source is readable typed C. It contains no assembly, raw-address
or byte-offset dereference, pointer/integer reconstruction, pointer or union
pun, inactive-union access, undefined overflow, volatile scheduling device,
force-inline annotation, optimizer pragma/barrier, synthetic anchor, or
object-byte forcing. The final COFF defines only two code COMDATs plus debug
metadata; it defines no `.rdata`, `.data`, `.bss`, COMMON symbol, or global
storage.
- Complete `halobetacache_build` and `libcmt_build`: pass.
- Direct hardened comparison: both retained functions pass; all four
rejected symbols are absent from the final COFF symbol table.
- Semantic audit: 470 units, 4,052 functions evaluated, 3,912 semantic
exact, 101 hidden exact / 61,876 hidden code bytes, 3,973 accepted exact,
and zero unit errors.
- Campaign progress: 375/833 complete objects, 3,960/11,060 exact functions,
475,638/2,198,102 meaningful code bytes, and
1,835,088/4,176,062 matched data bytes. This wave adds two functions and
71 meaningful bytes with no data increase.
- Admission audit: zero candidates and zero revocations; only the inherited
`source/shell/shell_xbox` completion-label contradiction remains.
- Parked validation: three active, zero stale, and zero invalid entries.
- Complete tooling suite: 179/179 tests pass; `git diff --check` is clean.
The fail-closed pre-wave check identifies exactly the two retained functions
as `NEWLY_EXACT` and reports `changed_nonexact: []`. Adding the first two
COMDATs necessarily changes the translation unit's debug section and symbol
set, which the generic structural check surfaces for review; direct hardened
comparison independently proves both accepted functions. No adjudication or
configuration exception is added.
A clean committed-state snapshot/check followed by deletion and forced
rebuilding of `action_flee.obj` is the final same-path reproducibility proof.
No push is performed.
@@ -1,133 +0,0 @@
# `action_flee.obj` opus5 150K wave 2: park re-investigation (2026-09-14)
## Scope and provenance
- Translation unit `source/ai/action_flee.c`; target `build/split/source/ai/action_flee.obj`
(January 2002 Xbox debug build, XDK 3911 / VC7 13.00.9254, repository flags unchanged).
- Lane worktree `C:\halo-worktrees\opus5-150k-house-clean-20260914`, wave w2. Worker notes:
`scratch/workers/actions_action_flee_etc.md`; scratch candidates in `scratch/workers/actions_action_flee_etc/`.
- Ledgers read first: `action_flee_obj_jonas_two_leaf_20260821.md`,
`action_flee_obj_update_semantic_owner_boundary_20260905.md`,
`action_flee_obj_threshold_leaf_reconciliation_20260908.md`,
`action_flee_obj_position_helpers_exact_20260909.md`, the three `config/parked.json` entries.
- `branch_sweep.py source/ai/action_flee`: 10 blobs, none better than the current file.
## Baseline (real file, unchanged by this wave)
`== exact 10 residual 3 unwritten 0`, `_point_from_line3d` guard passes. All three residuals are parked:
| Function | Target | Base | Relocs | Gate tag |
| --- | ---: | ---: | ---: | --- |
| `_action_flee_setup` | 272 | 256 | 12 | size, sha |
| `_action_flee_current_position_exposed` | 352 | 352 | 13 | sha |
| `_action_flee_perform` | 864 | 848 | 30 | size, sha |
## Result
Production file **unchanged** (every target is parked). A scratch candidate reopens all three parks:
| Candidate | Gate | Notes |
| --- | --- | --- |
| `scratch/workers/actions_action_flee_etc/flee_c1.c` | 12 exact / 1 residual / 0 unwritten | setup and exposed strict EXACT; perform 864/30 sha-only |
| `scratch/workers/actions_action_flee_etc/flee_c1diag.c` | **13 / 0 / 0** | c1 plus a diagnostic local `unit_is_speaking` prototype (NOT landable; proves the header proposal) |
Diff: `scratch/workers/actions_action_flee_etc/flee_park_reopen_c1.diff`. Every baseline EXACT row stays EXACT,
the guard passes, the owner census against `build/base` shows no new or dropped owners (the three `real_math`
select-any fallbacks are pre-existing), and `tools/fake_match_scan.py` reports 0 leads (HEAD also 0).
## Evidence per function
### Shared: `action_flee_find_flee_position` has a third parameter
HCEX.pdb (SHIP), `DIA2Dump -sym`, gives
`static void action_flee_find_flee_position(long actor_index, unsigned char respect_direction, struct flee_state_data *flee_state_data)`.
January's call sites prove the stack argument:
- setup +0xe1: `push 0; call; add esp,4`;
- perform +0x1a6: `mov ebx,[ebp+8]; push 1; call; add esp,4`.
The callee never reads it, so its body stays EXACT (640/23) with the unused parameter. The candidate declares
`boolean respect_direction` with a one-line disclosure comment. Setup passes `FALSE` and perform passes `TRUE`.
The old two-parameter prototype was an ABI error, not a tie. It cost setup its frame spill: EBX has to carry
`actor_index` into the call, so January spills the actor pointer to `[ebp-8]`.
### `_action_flee_setup`: strict EXACT (272/12, sha 1a415a0adece0dd5)
- The 3-parameter call fixes the size and the frame.
- January loads `force_state_entry` first and emits the stores after `csmemset` as +5, +8, +0xc, +4, +0x1c, +0.
The source order that reproduces this is: forced_flee_ticks, flee_firing_position_index = NONE, panic_type,
flee_from_last_visible_location, allow_occluded_points, flee_prop_index.
- This is store-order recovery for independent field initialisation, per the methodology case `lra_new`.
- Shapes: v1 (prototype only), v2/v3/v4/v5b/c/e (other orders) were sha-only; v5d was EXACT.
### `_action_flee_current_position_exposed`: strict EXACT (352/13, sha 498dc2c046073108)
- **Wrong field in base.** At +0x130, January copies prop_datum +0xbc into `approach_point`. In our `props.h`
layout, +0xbc is `body_position`: `definitely_located` is 0xb8, `abandoned_search` 0xbb, and `head_position` is
+0x104, which is the pointer January passes to `ai_test_line_of_sight`.
- Base copied `head_position`, the same error as the HCEA lift. The second use of `&prop->head_position` created
the extra `[ebp-8]` pointer home, the 0x14 frame (January: 0x10) and the EBX/EDI swap.
- The park's premise, "coherent EDI/EBX allocation, not missing behaviour", is refuted.
- +0x16..+0x22: `exposed = FALSE` is stored after `datum_get`. By the store/call-order law, `actor` is declared
before `exposed`.
### `_action_flee_perform`: strict EXACT only with the proposed prototype (864/30, sha d62f05304574f88e)
January facts that each correct the base:
| January bytes | Source fact |
| --- | --- |
| +0x40 `cmp [esi+2],0; jle; mov [esi+8],-1` | `if (flee_stationary_ticks > 0) { index = NONE; } else { ... }` (base had the inverted polarity) |
| +0x109 `mov byte ptr [eax+0x74], bl` | prop +0x74 is `currently_damaging_me`; base wrote `unopposable_enemy` (+0xa4, HCEA's misnamed lift) |
| +0x101/+0x106 `movsx ecx,cx` / `movsx ecx,dx` | `prop->perception = MAX(prop->audibility, prop->ineffability);` direct, int-promoted |
| visibility, perception, damaging, LOS stores | statement order visibility, perception, currently_damaging_me, line_of_sight |
| +0x255 `or eax,-1; cmp ecx,-1; je` | `cause_unit_index = NONE; if (flee_prop_index != NONE) cause_unit_index = prop_get(...)->unit_index;` |
| +0x27a fall-through | the not-yet-communicated flee event is the then-arm; the idle event is the else-arm |
| +0x2d3 `mov al,[edi+6]; test; jne 0x324` | the line-302 assertion is guarded by `if (!actor->meta.swarm)` |
| +0x1a9 `push 1` | `respect_direction = TRUE` |
| +0x1ea `test al,al; jne; mov [esi+0x10],al` | `unit_is_speaking` returns `boolean` |
The last row is an owner gap. No header declares `unit_is_speaking` (definition: `unit_dialogue.c:389`,
`boolean unit_is_speaking(`), so `action_flee.c` compiles it as a C4013 implicit `int` and emits `test eax,eax`.
With every other fix, the flee_c1 perform differs from January by that single instruction. The diagnostic
candidate, which adds a local prototype, is strict EXACT.
Shapes: v6 (polarity, field, cause, arm order, swarm guard) gave 848. v6p (+ diagnostic prototype) gave 848. v7
(perception after damaging) gave 848. v7b gave EXACT. v7n (v7b without the prototype) gave 864, sha-only.
## Proposals
1. **ORCHESTRATOR-PROPOSAL (header), `source/units/units.h`**. Insert after the `unit_notify_impulse_sound`
prototype, before `unit_make_damage_sound`:
```c
boolean unit_is_speaking(
long unit_index);
```
Evidence:
- January +0x1ea `test al,al`;
- the definition's return type;
- punpckhdq `units.h:557` declares the same prototype at the same neighbourhood.
Consumers: 66 direct includers (`scratch/workers/actions_action_flee_etc/units_h_consumers.txt`). The
declaration-count law applies, so a full build is required.
2. **park-reopen `_action_flee_setup` and `_action_flee_current_position_exposed`** from `flee_c1.c`. Both are strict
EXACT without any header change.
3. **park-reopen `_action_flee_perform`** from `flee_c1.c` once proposal 1 lands. If proposal 1 does not land, the
reopen of (2) still changes perform's call site (`push 1`), so perform's park must be re-baselined to
864/30 sha cefb8e42bcdf2d49.
## Do-not-repeat
- The two-parameter `find_flee_position` prototype.
- `head_position` as the exposure approach point.
- `unopposable_enemy` in perform's arrival block.
- The `<= 0` stationary polarity.
- A short perception local.
## Reopen criteria
- setup and exposed: met (January-proven ABI and field facts).
- perform: land the `unit_is_speaking` prototype in its owner header.
@@ -1,57 +0,0 @@
# `action_flee.obj` - opus5 150K house-clean lane, wave w3 (2026-09-14)
## Scope and baseline
- Translation unit `source/ai/action_flee.c`; target `build/split/source/ai/action_flee.obj` (January 2002 Xbox debug, VC7 13.00.9254).
- Worker notes: `scratch/workers/w3_path_smoothing_actor_firing_position_etc.md`; scratch dir `scratch/workers/w3_path_smoothing_actor_firing_position_etc/`.
- Baseline real-file gate (guard passes): `== exact 12 residual 1 unwritten 0`. The only residual, `_action_flee_perform` (864/30), is parked
(park base 864/30 `cefb8e42bcdf2d49`, target `d62f05304574f88e`).
- Ledgers read: `action_flee_obj_jonas_two_leaf_20260821.md`, `action_flee_obj_update_semantic_owner_boundary_20260905.md`,
`action_flee_obj_threshold_leaf_reconciliation_20260908.md`, `action_flee_obj_position_helpers_exact_20260909.md`,
`action_flee_obj_opus5_150k_w2_20260914.md`; laws `scratch/w2/laws_w2.md` (C2, A7, R8, R9).
- `branch_sweep.py`: best historical blob 10/3/0 (below the current file).
## Result
**The real file is unchanged** (final gate 12/1/0, guard passes, park drift 0/1, fake scan 0 leads). One header packet is proposed,
measured with a shadow-header consumer census.
## `_action_flee_perform` - park reopen via a genuine units.h packet (no body change)
Law A7 (missing prototype via the genuine owner). `CL /Zs /W3` (`scratch/w3/c4013.txt`) reports two implicit declarations:
`unit_start_running_blindly` (line 414) and `unit_is_speaking` (line 689). Their definitions are `units.c:3897`
(`void unit_start_running_blindly(long unit_index)`; only a units.c-local prototype exists) and `unit_dialogue.c:389`
(`boolean unit_is_speaking(long unit_index)`). No header declares either; `units.h` owns both prototype sections (UNITS.C and
UNIT_DIALOGUE.C). January +0x1ea `test al,al` proves the boolean return; punpckhdq `units.h:557` declares `unit_is_speaking`
right after `unit_notify_impulse_sound`.
Shadow-header method: `shadow_gate.py` compiles each unit inside a full copy of the `source/` headers (so every quoted include
resolves in the shadow tree) and dumps per-function size/relocations/normalized sha. 76 units transitively include `units.h`.
| trial | units.h change | action_flee | 76-consumer deltas vs pristine |
| --- | --- | --- | --- |
| P | none | 12/1, perform = park base | - |
| H1 | + `unit_is_speaking` only | 13/0 | perform -> target; speech_timers park load pair; `_physics_update_old` (non-exact); **`_unit_preprocess_node_orientations` EXACT -> residual** |
| H2 | + both prototypes | **13/0** | **exactly two**: perform -> target; `_ai_communication_update_speech_timers` park flips to its second documented flag-neutral load order (672/43 `885fdb81966284ac`) |
Sum of strict-exact rows over the 76 units: P 2402 -> H2 2403, zero losses. H1 is rejected (declaration-count victim).
ORCHESTRATOR-PROPOSAL (exact diff `scratch/workers/w3_path_smoothing_actor_firing_position_etc/units_h_packet_H2.diff`, CRLF):
```c
/* prototypes/UNITS.C, immediately before unit_stop_running_blindly */
void unit_start_running_blindly(
long unit_index);
/* prototypes/UNIT_DIALOGUE.C, immediately after unit_notify_impulse_sound */
boolean unit_is_speaking(
long unit_index);
```
Consumers: 66 direct includers (`units_h_direct_includers.txt`), 76 compiled transitive consumers (`units_h_consumers.txt`).
Same commit: unpark `_action_flee_perform` (864, no source change) and re-baseline the `_ai_communication_update_speech_timers`
park base to `885fdb81966284acf5ba7138a4bbbd2221e7c61c89114fdd6f53b96ba89b81a0`. A full ninja plus stable sweep is required.
## Reopen criteria
- `_action_flee_perform`: met when the H2 packet lands (not the single-prototype variant).
@@ -1,109 +0,0 @@
# `action_flee.obj` position-helper packet (2026-09-09)
## Result
Two previously unwritten January functions are now strict COFF matches in a
single, caller-connected translation-unit reconstruction:
| January owner | Recovered private name | Meaningful | Padded | Relocations | Normalized SHA-256 |
| --- | --- | ---: | ---: | ---: | --- |
| `_code_00003210` | `action_flee_find_flee_position` | 629 | 640 | 23 | `058dad92555bc68a3f7deb04f41c3b1995d70808b593c142c7f534383e18b0b7` |
| `_code_000039e0` | `action_flee_at_flee_position` | 318 | 320 | 13 | `85c493abe1c07f9d923b165c98d0ec59098a6c21ef04a0954f8c5451507804fa` |
The packet therefore contributes **947 meaningful exact code bytes** and
**960 padded exact bytes**. The focused gate reports 9 exact, 3 residual and
1 unwritten function in `action_flee.obj`; the inherited baseline was 7 exact.
No whole-object completion is claimed.
## Name and behavior evidence
The independent HaloCEA reconstructions at
`research-cache/halocea-full-blobs-20260830/src/blam/ai/` recover the three
private names and their caller relationships:
- `action_flee_find_flee_position.c`
- `action_flee_current_position_exposed.c`
- `action_flee_at_flee_position.c`
- `action_flee_setup.c`
- `action_flee_perform.c`
January remains the byte and branch-semantics authority. The first exact
owner carries January's line-522 non-swarm assertion, selects and commits a
cover/panic firing position, and computes an approach point through the real
path subsystem. It uses `actor_get`, `actor_definition_get`, `prop_get` and
`TAG_BLOCK_GET_ELEMENT`, plus declarations from the owning `ai.h`, `path.h`,
and `actor_perception.h`; no raw datum/tag casts or local foreign prototypes
were introduced.
The three ACTOR_PERCEPTION.C situation declarations live in the dedicated
owner header. Their earlier placement in the middle of the heavily shared
`actors.h` perturbed C2's declaration-position-sensitive allocation in
`units.obj` and reopened one otherwise exact owner. Isolating the declarations
keeps ActionFlee fully typed while preserving all 189 exact `units.obj`
functions and the existing code generation of unrelated AI consumers.
The two previously undeclared PATH.C routines use an opt-in declaration block
at the end of `path.h` for the same reason. The genuine `path_state_find`
return type is corrected in the ordinary owner declaration, while the two new
declarations are visible only to ActionFlee. This preserves the closer parked
`encounter_update_respawn` schedule instead of silently degrading a fuzzy
owner merely to expose unrelated declarations.
The second exact owner carries January's line-470 assertion, validates the
current encounter firing position, honors the already-at-destination fast
path, and compares the squared body distance against the actor's destination
tolerance. January's final branches show that a tracked prop with line-of-
sight value clear or occluded prevents this fallback arrival result; no prop,
or a different line-of-sight state, permits it. This differs from the later
HaloCEA lift's final polarity, so the donor was used only as a semantic/name
map and the January branch behavior was preserved. The final boolean is
normalized with the ordinary `result = !result; return result;` idiom; the
open Marathon cseries source uses the same toggle-then-return spelling in
`cseries.lib/macintosh_utilities.c`, so this is not an invented dependency or
optimizer directive.
## Honest residuals retained with zero credit
The coherent callers needed to make the private static owners emit are kept as
the best currently reviewed typed reconstructions, but they are not exact and
receive zero matched-byte credit:
| Function | January | Candidate | Relocations (target/candidate) | Objdiff |
| --- | ---: | ---: | ---: | ---: |
| `action_flee_setup` | 272 padded / 265 meaningful | 256 padded | 12 / 12 | 80.265305% |
| `action_flee_current_position_exposed` | 352 padded / 347 meaningful | 352 padded | 13 / 13 | 83.46342% |
| `action_flee_perform` | 864 padded / 857 meaningful | 848 padded | 30 / 30 | 77.46595% |
Their normalized target/candidate SHA-256 pairs are respectively:
- setup: `1a415a0adece0dd541b62c1c9ada51604d58751e66d769d88619d76db5471d79` /
`cfc7ace7bcb857227c8c0559c80681a4f9ff2b142fa9df3e57c59af75960a898`
- current-position exposure: `498dc2c046073108d309d0057bb5fc0405a8112c5532e90ef7ec5e79f21d7708` /
`bbfe7ce7c3bafd235c0528f0480f5836b1d5016ec27e364ef77db6381c616c60`
- perform: `d62f05304574f88eefd7c025a452910d821e3986442777adcb223dc0d2085dc2` /
`e4b2a3cecd2525f5989ea94abd238fd8cc687627248f91ba39f597fe4ae66f7e`
These are ordinary typed C with named constants and subsystem APIs. They
contain no volatile/register forcing, pragma, assembly, raw offset,
representation pun, fake dependency, undefined behavior or nonsensical
branch. They should be revisited only with authoritative January source/local
records or another natural same-compiler donor, not expression-order search.
## Verification boundary
The final PID-isolated campaign gate compiled only `source/ai/action_flee.c`,
compared every target function, and rejected any emitted
`_point_from_line3d` owner. The guard passed. Because the semantic symbol
renames are part of this packet while the shared split object intentionally
remained untouched, the focused gate used temporary in-memory aliases from
the recovered private names to the old `_code_*` split labels. No Ninja,
configure, shared build output or unrelated source file was touched.
The ordinary `real_math.h` distance expression also emits select-any fallback
copies of `_distance_squared3d` (64 bytes), `_vector_from_points3d` (48 bytes)
and `_magnitude_squared3d` (48 bytes). Each has zero relocations and exactly
matches the corresponding January-selected owner in `action_alert.obj`
(`f42fa39f289bfa50`, `905f56dbe4597d2b`, and `0d04579096eeab3a`
respectively). They receive no target-owned or progress credit; this packet
does not alter their ownership. No `_point_from_line3d` definition or
reference is emitted.
@@ -1,45 +0,0 @@
# ActionFlee small-leaf reconciliation (2026-09-08)
## Result
This reconciliation admits three complete Fable-lane leaves into their real
`action_flee.c` owner and moves their public declarations from the unrelated
`actions.c` dispatcher into the closest associated header, `actions.h`.
| January owner | meaningful bytes | padded bytes | relocations | verdict |
| --- | ---: | ---: | ---: | --- |
| `_action_flee_flush_position_indices` | 39 | 48 | 2 | exact |
| `_action_flee_modify_color` | 99 | 112 | 4 | exact |
| `_action_flee_replace_prop` | 48 | 48 | 2 | exact |
The batch adds **186 meaningful / 208 padded exact code bytes** and three
strict functions. The two inherited exact ActionFlee owners remain exact.
## Source and ownership review
The implementations use the existing typed `actor_get` accessor, the genuine
`flee_state_data` fields, `NONE`, project booleans, and the canonical ARGB
color owners. Parameters remain one per line and every void body ends with an
explicit `return;`. No inline control, assembly, raw offset, address-derived
name, fabricated dependency, or manual bit logic is introduced.
The declarations belong to `actions.h` because this translation unit has no
dedicated header. All ActionFlee declarations now live there, including the
setup/dispatch callbacks and `action_flee_blind_panic`; their duplicate local
declarations were removed from `actions.c` and `actor_type_grunt.c` without
changing either consumer's code owners.
## Focused evidence
The canonical source gate reports `5 exact / 0 residual / 8 unwritten` for
`action_flee.obj`; `actions.obj` remains `58 exact / 5 residual / 0 unwritten`.
Both gates reject any emitted `_point_from_line3d`, and the changed-file
fake-match scan reports zero findings. The packet author also compared all 22
other initially compilable `actions.h` consumers without a count change; the
enclosing canonical build compiles the complete consumer set. The separate
`ai_debug.c` Path-interface repair receives no ActionFlee proof or credit.
The full-build, rename-stable sweep, park validation, admission audit and test
suite are recorded in the enclosing reconciliation batch. These three bodies
receive exact credit only if that global admission gate reports no inherited
loss.
@@ -1,189 +0,0 @@
# `action_flee.obj` update semantic and owner boundary, 2026-09-05
## Result
`action_flee_update` is **withheld**. This review admits neither exact nor
fuzzy code and creates no park. The current production object remains at its
published two strict functions; the attempted update remains unwritten.
Two independent blockers were demonstrated:
1. The required public-API ownership repair changes two inherited strict Race
functions and the existing AI speech-timer park in a full 84-object header
trial. The entire four-file API packet was restored; there is no
caller-local declaration or header-position workaround.
2. The first natural 160-byte body writes the positive-panic timestamp to the
wrong, adjacent semantic member. January writes actor `+0x39C`; the
candidate writes `+0x398`. This is not an ordinary scheduling residual and
is not eligible for a fuzzy park.
The member discrepancy is now resolved by a complete neighboring-use census:
the existing layout is correct. Actor `+0x398` is
`emotions.last_flee_failed_time`, while `+0x39C` is
`emotions.flee_with_friends_disable_time`. A new source manifest containing
only that semantic member correction is preserved pending a future genuine
API context. It was deliberately not compiled against restored, incorrectly
owned declarations.
## January behavior and ABI
The authoritative January `_action_flee_update` is 152 meaningful / 160
padded bytes, has four relocations, and contains 54 decoded instructions.
Its normalized SHA-256 is
`c5e5c557ca1dc9c70ea3fe4dbfff11451fc6a5b7a1b71921f839386588fbe2d8`.
Its call/data relocations, in order, are `_actor_data`, `_datum_get`,
`_unit_start_running_blindly`, and `_game_time_get`.
The target establishes the following source contract:
- `actor_index` is a complete 32-bit datum handle. The prologue loads the
full dword argument and passes it to `datum_get`; the HCEA donor's
`uint16_t` parameter is not the January ABI.
- `actor->meta.unit_index` at actor `+0x18` is also consumed and passed as a
complete 32-bit handle.
- `forced_flee_ticks` at `+0x9C`, `flee_stationary_ticks` at `+0x9E`, and
`panic_type` at `+0xA8` are signed 16-bit values.
- `flee_timer` at `+0xB4` increments every call with 32-bit machine wrapping.
- A positive forced-flee countdown decrements once. A positive stationary
countdown decrements once; only its `1 -> 0` transition can start blind
running, and only when the unit is not `NONE` and the signed panic value is
in the inclusive `[9, 12]` interval.
- The blind-running decision naturally calls the already strict
`action_flee_blind_panic(short)` source helper. The compiler may inline its
range test; the helper must not be manually expanded or artificially
retained.
- Independently of the unit/countdown path, any signed `panic_type > 0`
calls `game_time_get`, adds 750 ticks (`25*TICKS_PER_SECOND`) modulo 32
bits, and stores the result at actor `+0x39C`.
- When both calls occur, `_unit_start_running_blindly` precedes
`_game_time_get`. The function returns `void` and preserves its callee-saved
state and stack.
## The adjacent fields are not a missing-layout gap
Three independent January-backed uses distinguish the two adjacent dwords:
- In `_action_flee_perform`, after setting the flee state's
`unable_to_flee` byte, the target calls `_game_time_get` and at function
offset `+0x1C3` stores EAX to actor `+0x398`.
- `_actor_action_handle_panic_transition` tests actor `+0x398` against
`NONE`, calls `_game_time_get`, reloads `+0x398`, adds seven ticks, and
compares it with the current time. Current source names that value
`last_flee_failed_time`.
- The strict January/canonical `_actor_stimulus_prop_fleeing` calls
`_game_time_get` and at function offset `+0x58` compares it with actor
`+0x39C`. Its natural typed source names that value
`flee_with_friends_disable_time`; the function is strict exact at
263 meaningful / 272 padded bytes and 14 relocations, normalized SHA-256
`13df393dee019d54339f963e17edd5d85610d11ce48fe5c2e357240b22f65da1`.
The canonical `actor_emotion_data` already declares those members in that
order. Its base is actor `+0x350`, so relative offsets `+0x48` and `+0x4C`
produce the observed actor offsets `+0x398` and `+0x39C`. The HCEA
`actor_emotion_data.h` independently corroborates both names and offsets, but
its `action_flee_update.c` incorrectly selects `last_flee_failed_time` and is
not authoritative. The extracted January PDB census contains no member-level
type record for these fields, so no direct PDB-name claim is made.
The behavior is also coherent: a live positive panic refreshes a 25-second
cooldown that suppresses group/friend-flee propagation, while a failed flee
attempt records its separate, short-lived failure time at `+0x398`. No
padding, field insertion, or four-byte schema shift is proposed.
## First natural body and executable behavior review
The first natural source used `actor_get`, the existing typed flee-state and
emotion layouts, the already strict blind-panic helper, and owner APIs. Its
focused gate was `2 exact / 1 residual / 10 unwritten`; both inherited strict
functions and the hard `_point_from_line3d` guard were preserved. The new
function is also 160 bytes with four relocations and 54 instructions. Its
normalized SHA-256 is
`6e9a8d4d811187492ebf30f9a1942589321ce1e5beedf2c30045564be563bb28`.
Aligned disassembly finds exactly one instruction difference:
```text
target +0x8F mov dword ptr [esi + 0x39c], eax
candidate +0x8F mov dword ptr [esi + 0x398], eax
```
The bounded Unicorn harness executes only `_action_flee_update` and stubs
only its three genuine call boundaries: `_datum_get`,
`_unit_start_running_blindly`, and `_game_time_get`. It verifies exact API
arguments, event order, all actor bytes outside target-written fields,
callee-saved registers, and stack balance. Its 47 cases cover:
- signed countdown boundaries `-32768, -1, 0, 1, 2, 32767`;
- `NONE` and full-width `0x89ABCDEF` unit handles;
- panic boundaries `-32768, -1, 0, 1, 8, 9, 10, 12, 13, 32767`;
- actor handles `0x00000007`, `0x12340007`, and `0xFFFF0007`;
- zero, sign-boundary, and all-one flee counters; and
- timestamp addition across 32-bit wrap.
The frozen January target passes **47/47**. The first candidate passes
**27/47** and fails exactly the 20 cases with positive panic. Every failure
has the same adjacent-field difference spanning actor bytes
`[0x398, 0x3A0)`: the candidate overwrites `last_flee_failed_time` and leaves
`flee_with_friends_disable_time` unchanged. Countdown behavior, complete
handles, calls, call order, and counter behavior agree in the remaining
observations. This is direct behavioral rejection of the candidate, not a
request to adapt the expected offset.
## Required owner-header repair and failed blast
A source-faithful production packet requires these ownership changes as one
coherent boundary:
- declare `void action_flee_update(long actor_index)` in `actions.h` and
remove its consumer-local declaration from `actions.c`;
- declare `void unit_start_running_blindly(long unit_index)` in `units.h`
and remove its owner-C local declaration from `units.c`; and
- include `game/game.h` from `action_flee.c` for the already owned
`game_time_get` API.
Root tested the pure owner-header migration across the complete 84-object
consumer union before admitting the body. It materially changed
`_race_touch_flag` (336 padded bytes) and
`_race_engine_player_update` (160 padded bytes), losing both inherited Race
stricts. It also changed the normalized body of the existing
`_ai_communication_update_speech_timers` park. The packet was therefore
withheld without header-position permutations, local-prototype fallback, or
shim headers.
After restoring all four API files, the full build returned to 6,217 strict
owners out of 8,245 with zero losses, and all 281 parks validated. Production
`action_flee.c`, the owner headers, and configuration remain unchanged by
this review.
## Frozen evidence
| Artifact | SHA-256 | Disposition |
| --- | --- | --- |
| `scratch/action-flee-update-target-frozen-20260905.obj` | `5D6E667103C4B42824A5E1C69FB047860A60D901EDA36153162CF19672EFF59E` | January target |
| `scratch/action-flee-update-natural-api-trial-20260905.obj` | `FA607D970E3E97D39519086C1FFA09008E98659211C35E2D2BF760D485E8B6D9` | rejected wrong-member candidate |
| `scratch/action_flee_update_natural_20260905.edits.json` | `2D9D0F48A831E2B2E9FF8EA1D1B8C1E6C2BAA797C79CDECA8CB8CD5B241B3197` | immutable first-source manifest |
| `scratch/action_flee_update_semantic_corrected_pending_20260905.edits.json` | `14D98FCD9964F932CDF92AB99364B974EEE009E4B75CB0B6A97C0577F980C58E` | one-member source correction, intentionally uncompiled |
| `scratch/action_flee_update_behavior_review_20260905.py` | `927D2B29FAF1A400B7429F60FE24146E8DC520CC71D617C156FD45E76DDAE1C2` | bounded behavior harness |
| `scratch/action-flee-update-target-behavior-reviewed-20260905.json` | `910C06F6630F38AC25B3B2E2F3A013BDC8CC577C717235515C98776FF620DE69` | target 47/47 pass |
| `scratch/action-flee-update-candidate-behavior-reviewed-20260905.json` | `EB6EC2F6A66739A828B6C2A2DCA431A9185EEF45015B427930DB1AB8CFF0D4C0` | candidate 27/47 pass, 20 semantic failures |
| `scratch/action-flee-api-failure-summary-20260905.json` | `6C68F7B76ACC6120F8D50A121FEAA206918763BCE5EEE83B69BACDEF168A88BD` | 84-object owner-header failure census |
| `scratch/action-flee-api-restored-20260905.json` | `5EF39E7AD811893F6E1FFAA2F46F960D06592AEDB144E958FF646E386B72CFB7` | restored stable census |
| `scratch/action-flee-api-parks-restored-20260905.json` | `92ED21DB874053C53684CDD519F70A2D1ACAAD96F76F9E0E8E0CD866879DB739` | restored 281-park validation |
| `scratch/actor-stimulus-prop-fleeing-canonical-20260904.obj` | `2FE9919CFE7DF2B7E8A7C6A895EB2DEECBA75FF12856BEB162E6377F1B731764` | strict `+0x39C` consumer |
Harness reproduction uses frozen objects only:
```text
python scratch/action_flee_update_behavior_review_20260905.py scratch/action-flee-update-target-frozen-20260905.obj --output scratch/action-flee-update-target-behavior-reviewed-20260905.json
python scratch/action_flee_update_behavior_review_20260905.py scratch/action-flee-update-natural-api-trial-20260905.obj --output scratch/action-flee-update-candidate-behavior-reviewed-20260905.json
```
## Reopen condition
Reopen only as a coherent owner-header trial after the Race and speech-timer
collateral can be reconciled by independently authenticated source/API
evidence. At that point compile the preserved corrected manifest once in the
genuine header context and repeat the behavior, full owner, no-point, park,
and whole-tree stable checks. Do not patch the old object, shift the actor
layout, restore caller-local externs, permute declarations, or classify the
wrong-member candidate as fuzzy progress.
@@ -1,152 +0,0 @@
# ActionObey public API and structure-flush leaf: independent admission review
## Conclusion
The frozen public-API owner packet is **admission-safe and runtime-neutral**. The structure-index flush leaf is also technically sound: it adds exactly one genuine public function owner, is a strict all-section match to January, preserves every inherited section, has the correct 32-bit C ABI and owner metadata, and matches January behavior in 1,024 bounded executions. It adds **46 meaningful / 48 padded code bytes** and no data credit.
One provenance caveat remains for the root's policy decision. The body is typed, idiomatic, semantically corroborated, and contains no byte-forcing construct, but the Fable report explicitly says its exact source shape corrected the earlier 2026-08-21 miss by introducing the local `state_data` pointer. It therefore must not be described as an independently natural first candidate. If admission requires first-shot provenance rather than an authenticated exact reconstruction, this leaf needs explicit adjudication; its object, ABI, runtime, API-owner, and no-surplus evidence otherwise pass.
This review does not cover the parallel shared swarm/enum schema packet. No production, donor, build, configuration, park, or frozen object was changed, and no compile or gate was run.
## Source packet and owner boundary
There is no `action_obey.h`. `source/ai/actions.h` is the genuine narrow owner because it already owns `action_specification`, all obey control/state records, the other action-family public declarations, and an explicit `prototypes/ACTION_OBEY.C` section. The frozen API change adds these nine target-public declarations there:
```c
boolean action_obey_command_list_setup(
long actor_index,
short command_list_index,
struct obey_state_data *state_data);
void action_obey_flush_command_indices(
long actor_index);
void action_obey_flush_structure_indices(
long actor_index);
void action_obey_advance_command_list(
long actor_index);
void action_obey_begin(
long actor_index);
boolean action_obey_perform(
long actor_index);
void action_obey_update(
long actor_index);
void action_obey_control(
long actor_index);
void action_obey_end(
long actor_index);
```
`action_obey_describe_command` was already present in that owner section. Every added symbol is an external January function (`storage=2`, `type=0x20`, `value=0`, NODUP function COMDAT). The signatures agree with the existing consumer-local declarations, the `action_specification` callbacks, January's ordinary 32-bit stack accesses, and the retained HCEA semantics. In particular, `action_obey_command_list_setup(long, short, obey_state_data *)` preserves the pre-existing `actions.c` declaration exactly.
The paired cleanup removes from `actions.c` only the now-owned local declarations for `action_obey_command_list_setup` and the five action-table callbacks (`begin`, `perform`, `update`, `control`, `end`). It later removes the local `actor_action_change` declaration from `action_obey.c`; `actions.h` already owns that exact three-argument declaration. Moving declarations does not create a definition, helper, or alternate ABI.
The three no-current-cross-TU-call declarations (`flush_command_indices`, `flush_structure_indices`, and `advance_command_list`) are not synthetic helpers: all three are genuine external January owners. Later/HCEA callers also corroborate their public nature. `perform` and `control` are presently unwritten in canonical, but `actions.c` genuinely references both from the obey action table; their prototypes are necessary public contracts rather than promises invented for this leaf.
The frozen public API source hashes are:
- before: `actions.h` `f62df99f9ad20305ed01ebd09e120378aa513874dc3b990c2a07775a41e98086`, `actions.c` `3dbb2e77c2f4fd40cc0376589b9d3024f47f7a47d6e8375d8b0e9a3a616e44a2`;
- after: `actions.h` `962973471e872cf3f159ef06d1f8209c9a962a8fae5cac8bbb18e2e7d21146ac`, `actions.c` `f6347eb2573a36f12cf0c9253da66504441b8743269e8847fd3866770571d7ec`.
At review time the current `actions.c` still has the frozen after hash, while `actions.h` has subsequently changed to `ea1bec48dfcc5bdad4dc01137e8c9d4a1f6aa878ee6f3eae4d0ef13e64aee62` from the root's parallel schema work. The nine API declarations themselves remain textually unchanged. This report makes no claim about that later schema content.
## Public API dependent audit
The capture script resolved all real `actions.h` dependencies from the Ninja graph and froze 47 objects. The before and after unit lists are identical. Independent review of the runtime summary found:
- 47/47 units preserved;
- 3,079 runtime sections unchanged;
- 54 changes classified solely as compiler-local label spelling across 22 units;
- zero review-required units;
- zero runtime sections added, lost, or genuinely changed;
- zero genuine function-owner changes and zero new helpers;
- zero COMMON additions/removals;
- zero new `_point_from_line3d` emission or reference.
The full stable verdict maps are byte-identical: both contain 8,245 entries and share SHA-256 `223b39e9a64198e10ff6dab9fe24a92b1d715b87e385c9389fee4188d6a0f458`. The captured park census is 284 active, 0 stale, 0 invalid. Thus the API move has no inherited exact/residual loss and no ordinary-link owner side effect.
Primary frozen files:
- `scratch/action-obey-public-api-before-20260905.json` — SHA-256 `22082d37d81366a681520949d9502107f7ea43b8e185f29936643cd07c89d0d5`;
- `scratch/action-obey-public-api-after-20260905.json` — SHA-256 `49d6f456321f119a67d25ac7324d82b4fc282d43cf1e86044f4a342e43284f65`;
- `scratch/action-obey-public-api-runtime-audit-20260905/summary.json` — SHA-256 `6f473b63e0918f9f085c664dbccebcac2c28668cfc2196a95e5a510504f70e9e`;
- `scratch/action-obey-public-api-parks-20260905.json` — SHA-256 `6c212dbc1d83de169bccde32a1c1ed3acc281d998bc8ec42ebe9e1b37e850cfa`.
## Leaf body and source authenticity
The current leaf is:
```c
void action_obey_flush_structure_indices(
long actor_index)
{
struct actor_datum *actor = actor_get(actor_index);
struct obey_state_data *state_data = &actor->state.action_data.obey;
if (state_data->complex_control.destination_valid)
{
state_data->complex_control.destination_surface_index = NONE;
}
return;
}
```
This is a genuine typed implementation. It uses the owning records, an existing typed actor accessor, the named valid/surface fields, and the repository's `NONE` constant. It contains no raw offset, cast reconstruction, aliasing trick, inline/optimizer directive, synthetic anchor, or surplus wrapper.
Three independent sources support the behavior:
1. January PC code loads the full dword argument from `[ebp+8]`, passes that full handle with `_actor_data` to `_datum_get`, reads the byte at actor `+0xCC`, materializes the obey-state base at actor `+0x9C`, and conditionally stores `0xFFFFFFFF` at state `+0x40` (actor `+0xDC`).
2. The retained HCEA function tests `obey.complex_control.destination_valid` and sets `destination_surface_index=-1` on true.
3. The adjacent accepted canonical obey wrappers use the same `actor` then `state_data` local source pattern.
The HCEA extraction spells this one function's actor parameter `uint16_t`, unlike the surrounding `int` functions. That later-platform/decompiler spelling is not PC ABI authority. January's dword load, the target relocation/call sequence, the callback-family contract, and the runtime harness's untruncated handle prove canonical `long` is correct.
The caveat is derivation history, not source semantics. The 2026-08-21 natural batch produced a different 46/48 candidate hash and removed the miss after one attempt. Fable later recorded that the exact body added the `state_data` pointer missing from that attempt. Because it explicitly consumed the previous mismatch boundary, this implementation cannot inherit the earlier ledger's first-shot label even though the resulting ordinary C body is authentic in form.
## Exact object and owner evidence
January target object SHA-256 is `0e4f889a35b97c44f6ae91e4906b547bd64f2ff12b9f399e02b46f02b69633fb`. The frozen owner-clean candidate object SHA-256 is `543ef02661eafabe28197ce23ddd345fdfb4818d543017219ce933d56562da0e`.
For `_action_obey_flush_structure_indices`, target and candidate agree on:
- 46 meaningful bytes and a 48-byte padded section (two terminal NOPs);
- normalized SHA-256 `9f0facf2a5e64545b5b323119ea1ce6c45813a307bf0fb256308aab64fe0db82`;
- relocation `+0x08`, `IMAGE_REL_I386_DIR32`, to `_actor_data`;
- relocation `+0x0F`, `IMAGE_REL_I386_REL32`, to `_datum_get`;
- `.text` flags `0x60501020`, external storage 2, function type 32, value 0, COMDAT selection 1.
The first leaf comparison (before to after) adds exactly one runtime section and one genuine function owner. It changes no inherited runtime section, adds/removes no COMMON, emits no forbidden point helper, and has no debug delta. The later owner cleanup (removing the redundant `actor_action_change` local declaration) preserves all 12 runtime sections and every program symbol identity; it creates no new helper or owner.
Frozen leaf chain:
- before manifest `ee343e4c289c0b81eaa4972b030f1f1db83a150d1cd13096a5393e82c3f0c91e` / object `10ee91a816e424347da9cc3ccf6c820e6f4b7f4f4aba09d303eef26a0b547c5f`;
- body-after manifest `928b2bde2a3aa4c568d726ff87457c2ff91ad64f47f0b8d22aa8fdc9c47ae426` / object `9edaf5bafdd56b1ab3864f7b3a777e6b8ad688084571346efc2b1b0d6429a531`;
- owner-clean manifest `d8435f09c9c20d28562d4c62b5b587a57df19a99c2961c6d3255313e6ee3f581` / object `543ef02661eafabe28197ce23ddd345fdfb4818d543017219ce933d56562da0e`.
The isolated replay object (`69ae82ce4432cbe0df25eeb0d4e5912e16e19c8527a3aef7e645d31fac58d238`) defines the nine inherited exact functions plus this leaf—ten function owners total—and no unexpected code owner. The full before/after runtime report identifies only the new leaf; the owner-clean comparison is completely unchanged.
## Runtime differential
The final scratch runtime harness tests target, owner-clean actual, and isolated objects over all 256 possible `destination_valid` byte representations and four prior surface values (`0`, `17`, `0x80000000`, `0xFFFFFFFF`), or 1,024 cases per object. All three produce trace SHA-256 `999e2273e77f3347e5871616830525f72c861071faf31f8e1757762f2767becd`.
It also verifies:
- one and only one `_datum_get(_actor_data, actor_index)` call;
- the full 32-bit handle reaches the accessor;
- the surface changes to `NONE` iff the valid byte is nonzero;
- no other modeled actor byte changes;
- callee-saved registers, stack cleanup, and direction flag behavior;
- exact target section and owner metadata before execution.
The stated limit is appropriate: this models a valid actor handle and the `datum_get` contract; it is not a whole actor subsystem or image-link test. There is no uncovered input branch inside this leaf once those API preconditions hold.
Evidence:
- `scratch/action-obey-structure-leaf-owned-runtime-20260905.json` — SHA-256 `65e17c98c27a46eb49b0e25e92e95984d7d9b291e9b8dc11ec60f80d632b3752`;
- runtime script SHA-256 `d686a0c4c2251909cc6d7d4428091f318f2f867c35841c09a5441a6ae034b704`;
- owner-clean all-section summary SHA-256 `97a66ae9554056837a796f433621a966546b8ade2f21fdde4da1da4d9ceb5bf6`.
## Accounting and admission statement
Before this leaf, canonical `action_obey.obj` had 9/27 exact functions and 439 meaningful / 480 padded code bytes. This packet advances it to **10/27 exact**, **485 meaningful**, and **528 padded** code bytes. The incremental claim is exactly one function and **46 meaningful / 48 padded bytes**. It claims no `.rdata`, `.data`, BSS, COMMON, or other data owner.
Technical verdict: **PASS** for API ownership, exactness, ABI, runtime semantics, inherited-owner preservation, and accounting. Provenance verdict: **review required only for the “natural first candidate” label**; the frozen record establishes that it is a later exact correction. Do not roll this caveat into the separate shared-schema trial, and do not use this leaf as blanket precedent for the larger Fable ActionObey donor cluster.
@@ -1,98 +0,0 @@
# ActionObey command-owner trial: rejected and restored
Date: 2026-09-05. Baseline: `c32247e7651d680dcd133d72b2fbf4e8009cc124`.
## Disposition
No source or byte credit from this shared-header trial is retained. Its full
12-file source delta was reversed with a scoped patch; no checkout/reset/clean
or donor edit was used. The completed earlier shared swarm/biped/API packet
and structure-cache leaf remain intact.
The trial moved actor fire-target/combat-status enums to `actors.h`, moved
the unit animation impulse domain to `units.h`, and put command modifier
domains plus complete animation/script/recording reference records in
`ai_scenario_definitions.h`. Corresponding local shadows in nine C files
were removed. Datum field types and layouts were not changed; vehicle seat
desire ownership and Units implementation/private-name cleanup were not added.
The source proposal's first patch accidentally contained a redundant
`struct ai_command_definition;` forward declaration. This was removed as
an editing correction before the measured after snapshot. It was not
retained or searched as a compiler-order device.
## Measured failure
Actual Ninja dependencies identified **86** consuming base objects. The
complete strict sweep fell **6,233 -> 6,232**: the previously exact
`source/units/units::_unit_preprocess_node_orientations`, 1,920 padded bytes,
became residual. The full owner comparison found:
- 6,270 unchanged runtime sections;
- 84 changes restricted to compiler-local label spelling;
- one changed runtime owner, the Units function above;
- no added forbidden `_point_from_line3d` definition/reference.
The trial was rejected rather than exchanging inherited exact progress for
new dependency coverage. No header ordering, source spelling, flags, or
local facade variants were searched to rescue it.
## Restoration proof
The restored full sweep is **6,233 exact**, zero gains and zero losses.
All **6,355** runtime sections across the 86 affected objects compare
unchanged after restoration. Units is back to 189/189 exact.
The first restored capture correctly refused when its Ninja-dependency
database hash changed during enumeration. No compiler processes remained;
the next unchanged-state capture succeeded. The failed attempt wrote no
manifest or object snapshot and was not accepted as evidence.
Frozen evidence paths, relative to the canonical repository:
- `scratch/action_obey_command_owners_capture_20260905.py`
- `scratch/action-obey-command-owners-before-20260905.json`,
SHA-256 `34c077c0b3cbe43476e5e9dadc1a24fc22d29c462db23976b8ae1b8a45d8702d`
- `scratch/action-obey-command-owners-after-20260905.json`,
SHA-256 `090874e7e335ae137a823e4de032de6122ca89bee8860bfd397fec166a08a404`
- `scratch/action-obey-command-owners-restored-20260905.json`,
SHA-256 `23e6ff746d10fa75f5c23449f16903138281cac98ca62b54da20053adcd4523a`
- corresponding `*-stable-before/after/restored-20260905.json` snapshots;
- `scratch/action-obey-command-owners-runtime-20260905/summary.json`;
- `scratch/action-obey-command-owners-restored-runtime-20260905/summary.json`;
- measured corrected-build and restored-build logs under the same prefix.
The capture folders preserve every proposed/restored source file and all 86
actual objects. This is a rejected trial, not proof that every source-authentic
owner reconstruction must fail.
## Corrections discovered during independent prerequisite review
The complete private `action_obey_command_begin` and
`action_obey_command_perform` proposals remain scratch-only and uncompiled.
Later direct PDB checks refined the initial proposed owner declarations:
- The true wait names are `_ai_atom_wait_modifier_alerted`,
`_ai_atom_wait_modifier_visible_enemy`, and
`_ai_atom_wait_modifier_told_to_advance`, values 0..2. The adjacent
"always" literal belongs to loop modifier zero, not a fourth wait value.
January's out-of-domain wait behavior still returns true.
- PDB records authenticate all fourteen unit animation impulse names and
the full three scenario reference layouts. Their `unused` fields are
genuine records, not fabricated opaque prefixes.
- PDB constants belong to unnamed enums. Descriptive named enum tags in the
initial proposal were reconstructions, not recovered source names.
- The fire-target terminal is DB-spelled `number_of_actor_fire_targets`;
the previously suggested uppercase spelling was a style reconstruction.
- Directly named crouch/targeting/initiative/die modifier constants can
replace the private begin proposal's remaining raw boolean-valued literals
when the proper shared owner is integrated.
These corrections are preserved in the prerequisite inventory and do not
authorize a definition-order or equivalent-spelling search. A future coherent
owner packet must use the refined evidence, preserve actual dependent
objects, and keep private functions private. No consumer-local enum/type/
prototype shadow or fake external private ABI is an acceptable workaround.
The independently acceptable smaller callback graph does not need this
failed owner packet; it is tracked separately.
@@ -1,176 +0,0 @@
# `action_obey.obj`: inline math wrapper trio (2026-08-26)
## Outcome
Three ordinary inline math wrappers are newly strict-exact:
- `_vector_from_points2d`;
- `_cross_product3d`;
- `_negate_vector3d`.
The wave adds three exact functions and 122 meaningful code bytes.
`action_obey.obj` advances from 6/27 to 9/27 exact functions and from 317 to
439 matched meaningful code bytes. It remains incomplete; no completion
label, semantic exception, adjudication, data credit, or comparator waiver was
added.
Source commit:
- `ef95dd016320fed49b3945c7443513b33bd005fe`
(`Match action obey inline math wrappers`).
## Frozen evidence
The untouched translation unit was frozen at commit
`f529d037bc66823234e5e7a48bfdda08563b1ae8` in:
- `build/audit/action_obey_inline_math_baseline_20260826.json`;
- manifest SHA-256
`6b18cb311648b730e7264cd328aaffe96cc12e534c256011ab9174adb54dca3a`.
The target functions have these authenticated January fingerprints:
| Function | Meaningful / padded bytes | Relocations | Normalized SHA-256 |
| --- | ---: | ---: | --- |
| `_vector_from_points2d` | 29 / 32 | 0 | `5c9ee61634eac376cfc65890719accfeb6e0f5db37c55ab93b4da88bcbfe4f80` |
| `_cross_product3d` | 60 / 64 | 0 | `660da515fee7a623e6230a78b71749517ee5ad376db4126e5c8fe79414d2623a` |
| `_negate_vector3d` | 33 / 48 | 0 | `3265a51c2e64caa7a94fc32386ad62d6a774c929993624a9e83f35fbc2f2afcd` |
This was a same-name, same-ABI transfer rather than a masked-shape guess.
Current rebuilt objects already emit each exact function from the canonical
inline bodies in `source/math/real_math.h`. Representative accepted donors
include:
- `vector_from_points2d`: `real_math.obj`, `collision_bsp.obj`, and
`collision_features.obj`;
- `cross_product3d`: `actor_type_infection.obj`, `ai_debug.obj`,
`players.obj`, `matrix_math.obj`, `random_math.obj`, `real_math.obj`,
`objects.obj`, `bsp3d.obj`, and `collision_bsp.obj`;
- `negate_vector3d`: `players.obj`, `real_math.obj`, `models.obj`,
`objects.obj`, and `render_sky.obj`.
The repository header independently authenticates the ordinary C bodies,
operand ordering, return types, and parameter types. All three targets have
zero relocations, so there is no unresolved owner or addend hypothesis.
## Candidate discipline
The three inline identifiers are locally macro-renamed across the include
block, then ordinary out-of-line definitions are appended after the already
accepted `_random_range` wrapper. Appending preserves every existing function
section index. The macros only avoid duplicate C definitions; they do not
change optimization, calling convention, or emitted instructions.
The one normal VC7 candidate compile was strict-exact on the first attempt.
There was no object permutation, compiler-flag change, allowlist,
adjudication, comparator exception, object patch, intrinsic, alignment
directive, synthetic anchor, or byte-forcing expression.
## Exact comparison and frozen gate
The hardened comparator reports `all_equal: true` for the three new wrappers
and all six previously accepted functions:
- `_vector_from_points2d`;
- `_cross_product3d`;
- `_negate_vector3d`;
- `_random_range`;
- `_action_obey_flush_command_indices`;
- `_action_obey_advance_command_list`;
- `_action_obey_begin`;
- `_action_obey_end`;
- `_action_obey_update`.
For each new wrapper, target and candidate agree on every padded byte,
normalized hash, zero-relocation inventory, and function-section ownership.
The frozen gate reports exactly the three wrappers as `newly_exact`, the six
accepted siblings as `still_exact`, and `changed_nonexact: []`. Its sole
expected structural finding is the additive symbol inventory from defining
the wrappers.
## Clean committed state and replay
The clean committed manifest is:
- `build/audit/action_obey_inline_math_committed_20260826.json`;
- commit `ef95dd016320fed49b3945c7443513b33bd005fe`;
- SHA-256
`c00d5c50ed2e757ddbdd96bbca96764cdc85e5a885fdc8466e96751f8a4581ed`.
Its pre-rebuild and post-rebuild checks return `ok: true`, no failures, no
warnings, no changed nonexact functions, and all nine functions above as
`still_exact`.
The generated object path was resolved inside this worktree. That single file
was deleted and rebuilt once from the committed source. Raw base-object hashes
were:
- before replay:
`318e7b5cf227a3b0f6ea4258074ce0b52b10201b90bbac8835900be0020945f9`;
- after replay:
`fe1ece76d2544a0d01b58cdd49927a0d962c616b591441ca8c48b57393e5aa63`.
VC7 debug/build records are nondeterministic, so raw object hashes are not the
equality oracle. The post-replay committed manifest is green and the direct
hardened comparison remains `all_equal: true`.
Pinned artifacts:
- target split object:
`0e4f889a35b97c44f6ae91e4906b547bd64f2ff12b9f399e02b46f02b69633fb`;
- source file:
`dbcc7c6d642babfa08ec5a1090661fc9fe35d50bb2f415700dd27d89350785dd`;
- source blob before/after:
`fed1a96e20b6e9e41353ef77085488e19929f948` /
`770920ff65e3ecddff29ba1d83b12f4b189ce89a`;
- canonical report:
`3bab75fe187a50dd5fe76f472abe144ab33e97bca3031f67d89162f59bbd3eec`;
- semantic report:
`8b568709e2d795289d4c420b475f6c6e0a80646925d53145ed21703cd029ee60`;
- VC7 compiler:
`483e00c47bb08d699475a642bcff15b5b2036350b31c540e88a506baf101da11`;
- hardened comparator:
`411e41698a9491a7b69430307d03f844ee3144633e5d3038d3ffde81a8eccfb7`;
- regression gate:
`d09b46dd2331ff7ca677cad78a66c780c4effa41a5389f0c614c9760d7632a5f`;
- matching methodology:
`3eb52f6b2df7b94b4d732161c0f86e2becebbcf0b546824deb957882118b1b5c`.
Supporting audits:
- admission SHA-256:
`f0aa45c22cc604fb74abc388b06dd79a27e7a0b498a5d28837a9e785680c9a36`;
- parked-frontier SHA-256:
`cfac03aea5be607c19a6a40bcdbc1164e71ffb0179602af4bd37b3fd50e26d1d`.
## Canonical progress and gates
After regeneration:
- canonical code: 508,669/2,198,102 bytes and 4,211/11,060 functions;
- complete files: 377/833;
- Halo category: 495,755/1,770,166 code bytes and 4,044/7,574 functions;
- Halo complete files: 275/468;
- semantic audit: 470 units and 4,319 functions evaluated, 4,176 semantic
exact, 114 hidden-exact functions / 64,705 hidden bytes, 4,237 accepted
exact, and zero unit errors.
Repository gates:
- object admission: zero candidates and zero revocations; only the inherited
`shell_xbox` completion-label contradiction remains;
- parked validation: three active, zero stale, zero invalid;
- tooling tests: 179/179 pass;
- Halo and libcmt dry graphs: no work;
- protected-path touches: zero;
- deleted tracked paths: zero;
- no push.
## House-rule audit
Every parameter is on its own line and each wrapper ends with an explicit
value return. The retained source is C89. It contains no assembly, `register`,
`volatile`, barrier, pragma, explicit intrinsic, attribute, raw address,
pointer/integer reconstruction, undefined behavior, synthetic anchor, or
byte-forcing form. These wrappers perform no tag or object access, so the
typed-access rules are not implicated.
@@ -1,133 +0,0 @@
# `action_obey.obj` Jonas public-lifecycle salvage
## Result
This bounded source-only wave starts from canonical commit
`3f2ba63dfb1258f7e36baaec69fe495bcefdc20f`. Six ordinary public,
one-argument stack-ABI bodies were emitted together in one natural XDK compile
and measured independently. Five were strict in that first complete batch and
are retained. `_action_obey_flush_structure_indices` missed and was removed
without a second source shape or tuning pass. `action_obey.obj` advances from
0/27 to 5/27 exact functions and from 0/11,585 to 290/11,585 meaningful code
bytes. It remains `NonMatching` and receives no data credit.
| Function | Target meaningful / padded | Relocations | First candidate normalized SHA-256 | Decision |
| --- | ---: | ---: | --- | --- |
| `_action_obey_flush_command_indices` | `106 / 112` | 6 | `32a82f8ac1a4940e43f61ca190846d4dd6d6d2970adedde76ab0e42837ca3c03` | **retained: strict exact** |
| `_action_obey_flush_structure_indices` | `46 / 48` | 2 | `685bb16bd7a8c09586002344b247d5aa94fe50371bf33b398b82896932470495` | rejected: target hash `9f0facf2a5e64545b5b323119ea1ce6c45813a307bf0fb256308aab64fe0db82` |
| `_action_obey_advance_command_list` | `46 / 48` | 4 | `e1cc926aa1c23e5f6c1e9bb46b326736e53bff9e460248611ef98d43f48a8faf` | **retained: strict exact** |
| `_action_obey_begin` | `46 / 48` | 4 | `e1cc926aa1c23e5f6c1e9bb46b326736e53bff9e460248611ef98d43f48a8faf` | **retained: strict exact** |
| `_action_obey_end` | `46 / 48` | 4 | `e1cc926aa1c23e5f6c1e9bb46b326736e53bff9e460248611ef98d43f48a8faf` | **retained: strict exact** |
| `_action_obey_update` | `46 / 48` | 4 | `e1cc926aa1c23e5f6c1e9bb46b326736e53bff9e460248611ef98d43f48a8faf` | **retained: strict exact** |
The five retained functions total 290 meaningful bytes, 304 padded bytes, and
22 relocations. Each passes the hardened comparator in padded size, every
normalized byte, and every relocation address, type, symbolic destination,
and addend. Explicit terminal `return;` statements were added to satisfy the
house source form; final validation replays prove that these style-only
statements do not change any retained section.
## Provenance and one-shot boundary
- Baseline and restored atomic `action_obey.c` Git blob:
`febbd96e39c7519dc21dd86a35bbe6fe7b6a4579`.
- Retained `action_obey.c` Git blob / stable committed-payload SHA-256:
`e0bb4361e9e4a4bf353e5cbf58037fee9b9e0169` /
`e9160d896d224885bc51bee44329aca58cee420efb52c45875be1cee6d76e4ca`.
The SHA is over `git cat-file blob e0bb4361...`, not checkout bytes.
- January executable SHA-256:
`4cc87b45f721270392a96f1674ed2b5cd4a7bb4355faeab4531d1cf1884d9520`.
- January split `action_obey.obj` SHA-256:
`0e4f889a35b97c44f6ae91e4906b547bd64f2ff12b9f399e02b46f02b69633fb`.
- The preserved atomic base object is only the restored 476-byte shell,
SHA-256
`0c7ed4643c2075fe416eacfa9a464d49c59b8d32241755cc743e953793824454`;
it is not a candidate oracle and preserves none of the historical bodies.
- The supplied historical commit `677defb9d9b4dd0ac4f20bc2c5dbd53bf3e8d7f5`
is the action-charge frontier. The independently authenticated action-obey
atomic ledger is `f164672e22f55ade17493b2030d561d806bab2ec`, whose parent is
`983f99f6ea9de2d43f4c17297017982ca42ac883`.
- Clean HCEA semantic-oracle commit:
`c168af2e747d3095d9a29418ae401f3a39544863`. The six screened body blobs are
`7c6b1802eda90af54c7c0c81a0b3136aeac3fdff` (flush command),
`bf1a73ee05723cd4d0fe9a63a6945f3fa1995d8b` (flush structure),
`6599ec2fbe3b660229e331b71b789bd9d79149c8` (advance),
`b9c9eef8510f14c386742e4405e2d7e7e52bf7ce` (begin),
`80fb2698ca87abb6b554f6bf4a8cc0e633c838fd` (end), and
`7ac747c1b0688fc4a38dc0a99d4ef63a84e9aaa7` (update).
- Compiler: Microsoft 32-bit C/C++ Optimizing Compiler 13.00.9254.1 for
80x86, SHA-256
`483e00c47bb08d699475a642bcff15b5b2036350b31c540e88a506baf101da11`,
with unchanged `/O2 /Oy- /DDEBUG /Dxbox` flags.
HCEA was used only for semantics. January's symbols, stack access,
disassembly, literals, exact byte counts, and relocations supplied the PC
source shape. Every selected target symbol is a COFF external function of type
`0x20`, reads its sole `long` argument from `[ebp+8]`, and returns with the
ordinary C stack ABI. Exactly one new exploratory six-body object was emitted.
The rejected body was then deleted; later builds are validation replays of the
retained source, not alternate candidate searches.
## Retained behavior and typed state
`action_obey_flush_command_indices` obtains the typed actor and its embedded
obey state, validates the command-list index against the current scenario, and
either marks the state finished and transitions to no action or invokes the
private individual iterator with the command-index flush callback. The
advance, begin, end, and update wrappers obtain the same typed state and invoke
that iterator with their respective private callbacks.
The translation unit locally declares the callback function type and ordinary
typed undefined functions. Compile-time proofs preserve the January layouts:
simple control `0x24`, complex control `0x58`, obey state `0x84`, and the obey
state at actor offset `0x9C`. It uses named fields and existing typed accessors.
There is no raw address, byte-offset dereference, pointer or union pun,
inactive-union access, volatile scheduling device, force-inline annotation,
optimizer pragma/barrier, assembly, synthetic anchor, or object-byte forcing.
## Fail-closed scope, signatures, and ownership
The final candidate defines exactly the five retained public functions. The
rejected `_action_obey_flush_structure_indices` definition is absent. The
other 21 January functions are also absent, including every private
`code_*` helper, perform/control/setup/describe, and generic inline emission.
The six private callbacks/iterator used by the retained bodies are ordinary
typed undefined relocations, never definitions.
The final object consists only of `.drectve`, `.debug$S`, and five `.text`
COMDATs. It defines no `.rdata`, `.data`, `.bss`, COMMON symbol, or other
storage owner; January's 1,748 data bytes remain wholly unclaimed. Only
`source/ai/action_obey.c` and this ledger change. No header, caller,
configuration, semantic exception, parked record, existing matching log, or
protected source changes, and no path is deleted.
The current model is consumer-safe without a shared declaration. The only
textual begin/update/end references are in the disabled action table in
`actions.c`, while `actions.h` already models the relevant action callbacks as
ordinary `void(long)` functions and declares no conflicting cross-TU
signature. The private callback declarations likewise have no definitions or
conflicting declarations elsewhere in production source.
## Validation
- Complete `halobetacache_build` and `libcmt_build`: pass (569/569 actions).
- Direct hardened final comparison: all five retained functions remain strict;
the rejected wrapper, the other 21 target functions, and all data owners are
absent.
- Semantic audit: 470 units, 4,077 functions evaluated, 3,937 semantic exact,
101 hidden exact / 61,876 hidden code bytes, 3,998 accepted exact, and zero
unit errors.
- Campaign progress: 375/833 complete objects, 3,985/11,060 exact functions,
476,985/2,198,102 meaningful code bytes, and
1,835,088/4,176,062 matched data bytes. Halo contributes 3,818/7,574
functions and 464,071/1,770,166 code bytes.
- Object admission: zero candidates and zero revocations; only the inherited
`source/shell/shell_xbox` completion-label contradiction remains.
- Parked validation: three active, zero stale, and zero invalid entries.
- Complete tooling suite: 179/179 tests pass.
- `git diff --check`, deleted-path, and banned-policy scans are clean.
A clean committed-state regression snapshot/check followed by deletion and
forced rebuilding of `action_obey.obj`, another regression check, and a direct
hardened replay is the final same-path reproducibility proof. No push is
performed.
@@ -1,162 +0,0 @@
# `action_obey.obj`: random-range wrapper (2026-08-26)
## Outcome
`_random_range` is newly strict-exact. The wave adds one exact function and
27 meaningful code bytes. `action_obey.obj` now reports 6/27 exact functions
and 317 matched meaningful code bytes. It remains incomplete; no completion
label, semantic exception, adjudication, or data credit was added.
Source commit:
- `7bce0f67e990e5dd75e4392cbc9e6cfa4c32f6cf`
(`Match action obey random range`)
## Frozen evidence and donors
The untouched translation unit was frozen at commit
`3f2e6a76cb9cf575954a57e1bf3813cd312b0025` in:
- `build/audit/action_obey_random_range_baseline_20260826.json`;
- manifest SHA-256
`cc41ccbacdd5ab85bced427218def879a69dab6df5cc8f13fe9a051070f670de`.
January's `_random_range` is 27 meaningful / 32 padded bytes, with normalized
SHA-256
`7ed2e50e8e1a701a98fcceeb6a6b67e05d997f21bdb9ff5ee2e848fd1a8f409a`.
It has exactly two `IMAGE_REL_I386_REL32` relocations:
- byte 12 to `_get_global_random_seed_address`;
- byte 18 to `_seed_random_range`.
The repository shape atlas independently finds two exact 32-byte, two-call
donors with identical relocation-masked instruction bytes:
- `source/ai/action_alert:_real_random_range`;
- `source/camera/bored_camera:_real_local_random_range`.
The authenticated inline definition in `source/math/real_math.h` fixes the
recipient's short-returning semantics and exact destinations:
```c
return seed_random_range(
get_global_random_seed_address(),
lower_bound,
upper_bound);
```
Thus the transfer is jointly proven by January's bytes and relocations, two
accepted ABI-shape donors, and the canonical repository source body. It is
not a masked-byte guess.
## Candidate discipline
The header inline identifier is renamed locally across the include block, then
the ordinary out-of-line wrapper is defined after all five previously accepted
functions. The macro only avoids a duplicate C definition; it does not alter
optimization, calling convention, or emitted instructions.
The one normal VC7 candidate compile was strict-exact. There was no
emitted-object permutation, compiler-flag change, allowlist, adjudication,
comparator exception, object patch, raw intrinsic call, or byte-forcing
expression.
## Exact comparison and frozen gate
The hardened comparator reports `all_equal: true` for the new wrapper and all
five previously accepted functions:
- `_random_range`;
- `_action_obey_flush_command_indices`;
- `_action_obey_advance_command_list`;
- `_action_obey_begin`;
- `_action_obey_end`;
- `_action_obey_update`.
For `_random_range`, target and candidate agree on all 32 padded bytes,
normalized hash, relocation count, both relocation addresses/types, and both
exact destinations. The pre-commit frozen gate reports `_random_range` as
`newly_exact`, all five accepted siblings as `still_exact`, and
`changed_nonexact: []`. Its sole hard finding is the expected additive symbol
inventory caused by defining the new function.
## Clean committed state and replay
The clean committed manifest is:
- `build/audit/action_obey_random_range_committed_20260826.json`;
- commit `7bce0f67e990e5dd75e4392cbc9e6cfa4c32f6cf`;
- SHA-256
`506b7f5ff0129697a6a59056c2bf4783c36f9ccce32de4ea2346b4fa3fa60ed0`.
Its pre-rebuild and post-rebuild checks both return `ok: true`, no failures,
no warnings, no changed nonexact functions, and all six functions above as
`still_exact`.
The generated object path was resolved inside this worktree. That single
generated file was deleted and rebuilt exactly once. Raw base-object hashes
were:
- before replay:
`8d320430f41df2e30729f3f419bca7c16091972b06fef62273d4b6c4bfb630c0`;
- after replay:
`09f54824c750107064118aabebf38a202c03c236cd83ba5e423c4816a4fffdf3`.
VC7 debug/build records are nondeterministic, so raw object hashes are not the
equality oracle. The post-replay committed manifest is green and the direct
hardened comparison remains `all_equal: true`.
Pinned artifacts:
- target split object:
`0e4f889a35b97c44f6ae91e4906b547bd64f2ff12b9f399e02b46f02b69633fb`;
- source file:
`2794ea8b908210ea9bd5a94693ede143c95db1850bac1803ddb33f8269148f83`;
- source blob before/after:
`e0bb4361e9e4a4bf353e5cbf58037fee9b9e0169` /
`fed1a96e20b6e9e41353ef77085488e19929f948`;
- canonical report:
`28e97215980d2cb6afb13e04cffda0b6ed2d893155847215138c2925250ea822`;
- semantic report:
`4e06bd4620272dbf7d9eebcf36e682271599afae808d7536152cc834031dbc7a`;
- VC7 compiler:
`483e00c47bb08d699475a642bcff15b5b2036350b31c540e88a506baf101da11`;
- hardened comparator:
`411e41698a9491a7b69430307d03f844ee3144633e5d3038d3ffde81a8eccfb7`;
- regression gate:
`d09b46dd2331ff7ca677cad78a66c780c4effa41a5389f0c614c9760d7632a5f`;
- matching methodology:
`3eb52f6b2df7b94b4d732161c0f86e2becebbcf0b546824deb957882118b1b5c`.
## Canonical progress and gates
After regeneration:
- all categories: 23.13% matched and 14.21% linked, 377/833 complete files;
- code: 508,520/2,198,102 bytes and 4,207/11,060 functions;
- data: 1,836,762/4,176,062 bytes;
- Halo category: 4,040/7,574 functions;
- semantic audit: 470 units and 4,315 functions evaluated, 4,172 semantic
exact, 114 hidden-exact functions / 64,705 hidden bytes, 4,233 accepted exact,
and zero unit errors.
Repository gates:
- object admission: zero candidates and zero revocations; only the inherited
`shell_xbox` completion-label contradiction remains;
- parked validation: three active, zero stale, zero invalid;
- tooling tests: 179/179 pass;
- Halo and libcmt dry graphs: no work;
- protected-path touches: zero;
- deleted tracked paths: zero;
- added-source banned-construct matches: zero;
- no push.
## House-rule audit
Each parameter is on its own line and the function ends with an explicit
return statement. The source is C89. The retained code contains no assembly,
`register`, `volatile`, barrier, pragma, explicit intrinsic, attribute, raw
address, pointer/integer reconstruction, undefined behavior, synthetic anchor,
or byte-forcing form. This wrapper performs no tag or object access, so the
typed-access rules are not implicated.
@@ -1,114 +0,0 @@
# ActionObey (opus5 100K consolidated lane, waveB, 2026-09-14): describe_command and command_begin closed
Worker lane `opus/100k-consolidated-20260914` (worktree `C:\halo-worktrees\opus5-100k-consolidated-20260914`).
Only `source/ai/action_obey.c` changed. Two already-written residuals became strict EXACT;
`_action_obey_control` remains a documented register tie.
## 1. Scope and provenance
- Unit `source/ai/action_obey.c` vs January split `build/split/source/ai/action_obey.obj`.
- Compiler VC7 13.00.9254 (XDK), repository flags for the unit (`/c /O2 /Oy- /DDEBUG /Dxbox`), unchanged.
- Names were already authentic (January map atlas, orchestrator commit `47d9dd0da`); no rename needed.
- Evidence: January COFF (relocations, instructions via `tools.campaign.alndiff`), Ghidra decompilation
`C:/halo-worktrees/opus5-30k-fresh-graphs-20260914/scratch/ghidra/out/action_obey.obj.decomp.c:300-538`,
HCEA `research-cache/halocea-full-blobs-20260830/src/blam/ai/action_obey_command_begin.c` and
`action_obey_control.c` (semantics/order hints only).
- Prior ledgers read: `action_obey_obj_opus5_fresh_graphs_20260914.md` (including the lead admission),
`action_obey_obj_private_closure_plan_20260905.md`, `action_obey_obj_api_leaf_independent_review_20260905.md`,
and the fresh-graphs worker report.
## 2. Validated baseline (lane HEAD)
`== exact 24 residual 3 unwritten 0 (of 27 listed)`, `_point_from_line3d` guard passed, no parked entries.
| Row | Target (padded / relocs / sha16) | Baseline candidate | First divergence |
| --- | --- | --- | --- |
| `_action_obey_command_begin` | 4320 / 167 / `5b56fdd8b88a4456` | 4336 / 167 | `sub esp,0x184` vs `0x174` |
| `_action_obey_control` | 992 / equal | 992 / equal, sha differs | +0x33e register pair |
| `_action_obey_describe_command` | 1936 / 153 / `d14c27f63086d980` | 1824 / 147 | pause arm `jmp` into a shared tail |
Final: `== exact 26 residual 1 unwritten 0 (of 27 listed)`, guard passed, 0 parked drift (no entries), owner
census clean (no new code/data owner vs `build/base`; the header-inline SELECT_ANY math copies are pre-existing and
already disclosed), `tools/fake_match_scan.py` 0 leads.
## 3. Accepted controls (retained in production)
### `_action_obey_describe_command` (now EXACT 1936/153)
- `relocdiff --count-by-target`: every literal/call count equal except `__snprintf` 26 vs 20, so the gap was six
cross-jump merges, not missing code.
- January's animate and recording NONE arms read `mov eax, offset "NONE"; push eax` after
`mov eax, offset "<error>"`: the name is a variable, exactly like the script arm. Source now uses the
script-arm idiom for all three tag-reference atoms: `name = "<error>"; if (index == NONE) name = "NONE";
else if (valid) name = element; _snprintf(..., name);`. VC7 duplicates that small join (snprintf plus return)
into both predecessors; the script join also loads a modifier table, so it is not duplicated. Ghidra shows two
calls only because it decompiles the duplicated join.
- HEAD's separate `_snprintf(..., "NONE")` calls pushed an immediate. That made those arms identical to other
0x10 tails and moved VC7's cross-jump hosts across the whole function.
### `_action_obey_command_begin` (now EXACT 4320/167)
1. **Look-arm duration re-store.** January's `_ai_atom_look` valid-point arm contains
`mov ecx,[command+4]; mov [ebp-0x28],ecx` next to the point-index store, so `look_duration = command->parameter1`
is really stored twice. The same shape appears in look_random, which assigns the duration inside its valid arm.
The earlier lead removed this line as a "duplicated store". The instruction is in January, and removing it
again measures [sha].
2. **Declaration order.** The look prologue stores point, prop, unit, then duration (-0x14, -0x30, -0x2c,
-0x28), so `look_duration` is declared after the three NONE indices. The old order measures [sha].
3. **Scoped prop search.** In January, `prop_iterator` (-0x1c..-0x14) and the per-player `head_position`
(-0x20..-0x14) use overlapping storage, so their scopes are disjoint. In HEAD the prop iterator was scoped to the
whole look_player branch, which enclosed the player loop. That pushed `data_iterator` into its own 16-byte class
(frame +16) and split January's {direction, facing, head_position, vehicle_origin, prop_iterator,
actor_definition} class. The prop search is now in its own block inside the look_player branch. Without it the
size is 4336.
4. **running_jump store order.** `delay_ticks` is written before `pause_timer`, matching the already-exact
targeted_jump arm, which writes delay_ticks, the velocities, then pause_timer. This fixes January's EAX reload of
`simple_control` after `test al,al`.
## 4. Experiment matrix
| Candidate | Change | Result | Kept |
| --- | --- | --- | --- |
| describe v1 | animate/recording as script-style if / else-if + one `_snprintf` | 1936/153 EXACT | yes |
| begin b1 | + look-arm duration re-store | 4336, look diffs reduced | yes (step) |
| begin b2 | + duration declared last | 4336, only frame + running_jump register | yes (step) |
| begin b3 | + prop search block | 4320/167, frame 0x174, one EAX/ECX pick | yes (step) |
| begin b4 | + delay_ticks before pause_timer | EXACT | yes |
| begin n1 | b4 minus re-store | [sha] | no |
| begin n2 | b4 with old declaration order | [sha] | no |
| begin n3 | b4 without prop block | [size 4336!=4320] | no |
| control c1 | alignment_vector declared before facing | identical to HEAD | no |
| control c2 | HCEA ternary `jump_alignment_vector = normalize2d(...) == 0 ? *global_forward2d : facing;` first | worse (264 insns, register swaps spread to 0x2b7) | no |
| control c3 | same ternary into the local, store in place | identical to HEAD | no |
Every whole-TU gate kept all other rows unchanged (full `--all` listing diffs).
## 5. Do-not-repeat
- describe_command: the return/break mixes from the earlier lane. The cause was the NONE-arm shape, now closed.
- control: declaration order of facing/alignment_vector; ternary alignment forms; alignment store position;
Ghidra single overwritten local; jump-block scope local.
## 6. Residual classification
- `_action_obey_control` (992, [sha]): ABI/register tie, measured. January gives alignment i->EDX and j->ECX
(facing arm loads j first; join stores +0x448 before +0x444). Ours gives the inverse. Every other instruction is
equal.
## 7. Reopen criteria
- control: a verified January idiom where an enregistered 2D vector pair is copied j-first, or new type evidence for
`orders.move.jump_alignment_vector`.
## 8. Disposition
- `_action_obey_describe_command` and `_action_obey_command_begin`: strict EXACT, eligible for Matching
(normalized sha16 equal to January).
- `_action_obey_control`: still active residual, no credit.
- Lesson: both closed rows had been labelled cross-jump/frame-packing ties. In each case the relocation count or
slot overlap pointed to a concrete source shape. Read `relocdiff --count-by-target` and slot overlaps before
classifying a tie.
## Orchestrator admission note (checkpoint 2)
Only `_action_obey_describe_command` was admitted (1,936 padded). The strict-exact `_action_obey_command_begin` form restores `look_duration = command->parameter1;` in the look arm, directly after the identical declaration initializer. That is a redundant store, prohibited by the house rules, and the fresh-graphs lane removed it for the same reason. Its hunks, including the bare prop-iterator block and the pause_timer/jump reorder, were reverted. The worker file is preserved as `scratch/orch/action_obey_waveB.c`. Reopen only with evidence of a non-redundant January statement shape.
@@ -1,88 +0,0 @@
# ActionObey (opus5 150K house-clean lane, wave w2, 2026-09-14): command_begin blocker proved, control left exhausted
Worker lane `opus/150k-house-clean-20260914` (base `12f7375d4`). Only `source/ai/action_obey.c` was in scope.
**No production source changed.** `git diff --stat -- source/ai/action_obey.c` is empty, and the final real-file gate
matches the baseline row for row.
## 1. Baseline (real file, whole-TU gate, `_point_from_line3d` guard passed)
`== exact 25 residual 2 unwritten 0 (of 27 listed)`. This unit has no `config/parked.json` entries.
| Row | January (padded / relocs / sha16) | Real file |
| --- | --- | --- |
| `_action_obey_command_begin` | 4320 / 167 / `5b56fdd8b88a4456` | 4336 / 167, frame `0x184` vs `0x174` |
| `_action_obey_control` | 992 / equal | 992, [sha] only |
Inherited exact rows from earlier lanes were not touched: `_vehicle_possibility_qsort`, `_action_obey_describe_command`,
`_action_obey_command_perform`, `_action_obey_individual_perform`, `_action_obey_perform`.
## 2. `_action_obey_command_begin`: NOT LANDED (policy-blocked, reopen criterion proved unsatisfiable)
Prior state: the 100K lane's exact file (`opus5-100k-consolidated-20260914/scratch/orch/action_obey_waveB.c`) differs from
the real file in four hunks. The orchestrator rejected it twice because one hunk restores
`look_duration = command->parameter1;` inside the `_ai_atom_look` valid arm, a store that repeats the declaration
initializer. The admission note asked for a non-redundant January statement shape.
### January evidence (target COFF via `tools.campaign.alndiff`)
- Look prologue `+0x1ca..0x1e2`: `mov ecx,[ebp-4]` (command), `mov edx,[ecx+4]` (parameter1). It then stores the three
NONE indices and `mov [ebp-0x28],edx` (duration).
- Look valid arm `+0x205..0x213`: `mov eax,[ebp-4]; mov edi,ecx; mov ecx,[eax+4]; mov [ebp-0x14],edi; mov [ebp-0x28],ecx; jmp +0x418`.
- Across the whole function, `[ebp-0x28]` has exactly six uses:
- writes: `+0x1e2` (prologue), `+0x210` (look valid), `+0x2a4` and `+0x2b9` (look_random valid, `fstp`);
- reads: `+0x418` (the join, `fld` then compare against 0.0) and `+0x4fa` (pause_timer).
- Neither look_player nor look_object writes it. All six invalid look/look_random exits (`+0x1ee`, `+0x1ff`, `+0x229`,
`+0x23a`, `+0x24a`, `+0x255`) jump straight to `+0x418` without a store.
### Dominance argument
The join reads the duration first on every path. On the invalid edges and on the look_player/look_object paths, the only
reaching definition is the prologue store of `command->parameter1`. The source must therefore contain a dominating
`look_duration = command->parameter1` (the declaration initializer or an equivalent statement). The only alternative is
reading an uninitialized local, which is undefined behaviour and prohibited, and VC7 would not synthesize the prologue store
in that case anyway.
The look-valid store copies the same value, from the same memory, into the same variable. Nothing writes `[ebp-0x28]` or
`command->parameter1` between the two stores. Every C statement that emits these three instructions is therefore
value-redundant.
HCEA (`halocea-review/src/blam/ai/action_obey_command_begin.c:261-267`, a later build) has no such store, so it offers no
alternative January spelling. **The requested reopen criterion cannot be met on January's bytes.**
### Measurements (reference only, whole-TU with guard)
| Candidate | Change | Result |
| --- | --- | --- |
| `b4_waveB.c` | 100K exact form (includes the re-store) | 4320/167 EXACT; all other rows unchanged. Not landable. |
| `n1_no_restore.c` | b4 minus only the re-store | 4320 [sha]. The three look-arm instructions are missing, and the -0x14/-0x28/-0x2c slot permutation cascades through the look_random, look_player and look_object arms. |
No further shapes were tried: the dominance argument covers the remaining space. Classification: policy-blocked. January
contains a value-redundant store, so this is not a register or scheduling tie.
### Disposition and reopen
- Stays a zero-credit fuzzy genuine caller in the real file.
- The row can close only through an explicit owner decision to admit a January-proven redundant store. The evidence for
that decision is the 100K exact file plus the proof above. Do not requeue it for workers.
## 3. `_action_obey_control`: SKIPPED (exhausted, on the brief's do-not-spend list)
No new source or type evidence was found, so no shapes were spent.
Re-reading the four differing instruction pairs (`+0x33e..0x391`) gives one refinement to the 100K classification:
- In both builds, the pair member held in ECX is loaded first in the facing arm and stored first at the join.
- The `global_forward2d` arm loads in memory order in both builds.
So the "j-first copy, +0x448 before +0x444" ordering follows from the register choice (January: i in EDX, j in ECX; ours:
the reverse). It is not a separate statement-order fact. The residual is a pure ECX/EDX permutation of an enregistered
`real_vector2d` (class C). HCEA `action_obey_control.c:170-180` uses the same ternary-copy semantics that were already tried.
Reopen criterion is unchanged from the 100K ledger: a verified January idiom that copies an enregistered 2D pair with the
second member in ECX, or new type evidence for `orders.move.jump_alignment_vector`.
## 4. Final checks
- Final real-file gate equals the baseline (`diff` of the `--all` listings is empty); guard passed.
- No parks for this unit, so no park drift; owner census unaffected (no edit); `tools/fake_match_scan.py` reports 0 leads.
- Worker notes: `scratch/workers/action_obey.md`; candidates and objects: `scratch/workers/action_obey/`.
@@ -1,178 +0,0 @@
# ActionObey fresh-graphs lane (opus5, 2026-09-14): header-blocked private graph, measured in scratch
Worker lane `opus/30k-fresh-graphs-20260914` (base `c3e257e9a`, lane HEAD `47d9dd0da`).
**No production source changed.** `git diff -- source/ai/action_obey.c` is empty; the
final real-file gate equals the baseline. All progress below is scratch-only evidence
for an orchestrator header packet.
## 1. Scope and provenance
- Unit `source/ai/action_obey.c` (SHA-256 `bfc668a6...5fe9`) vs January split
`build/split/source/ai/action_obey.obj` (SHA-256 `d52f369d...600e`).
- Compiler VC7 13.00.9254 (XDK), flags `/c /O2 /Oy- /DDEBUG /Dxbox`.
- Names: January map atlas `scratch/atlas/action_obey.obj.txt` (all seven non-exact rows
already carry their authentic names after orchestrator commit `47d9dd0da`).
- Semantics: HCEA `research-cache/halocea-full-blobs-20260830/src/blam/ai/action_obey_command_{begin,perform}.c`,
`action_obey_individual_perform.c`, `action_obey_control.c`; Ghidra
`scratch/ghidra/out/action_obey.obj.decomp.c`; canonical uncompiled proposals
`scratch/action_obey_command_{begin,perform}_proposal_20260905.c`; Fable donor
`C:/halo-worktrees/fable-50k-small-families-20260904` (`f9dd42218`). No HCEA/Stian body
exists for `action_obey_describe_command` (PC debug only).
## 2. Validated baseline (lane HEAD)
`== exact 20 residual 1 unwritten 6 (of 27 listed)`, `_point_from_line3d` guard passed,
no parked entries for this unit.
| Row | Target | Status |
| --- | ---: | --- |
| `_action_obey_command_begin` | 4320 / 167 relocs / `5b56fdd8b88a4456` | UNWRITTEN |
| `_action_obey_command_perform` | 1408 / 51 / `afc109dc8ddf7e3f` | UNWRITTEN |
| `_action_obey_control` | 992 | residual [sha] |
| `_action_obey_describe_command` | 1936 / 153 / `d14c27f63086d980` | UNWRITTEN |
| `_action_obey_individual_perform` | 288 / 11 | UNWRITTEN |
| `_action_obey_perform` | 192 / 8 | UNWRITTEN |
| `_vehicle_possibility_qsort` | 64 / 0 | UNWRITTEN (no caller emits it) |
## 3. Results (scratch whole-TU gate, prerequisites declared scratch-only)
`scratch/workers/action_obey/b10.c`: `== exact 24 residual 3 unwritten 0`, all 20 baseline
exact rows still exact, point guard passed, `tools/fake_match_scan.py` 0 leads.
| Row | Scratch result | Candidate text |
| --- | --- | --- |
| `_action_obey_perform` | **strict EXACT** | `scratch/workers/action_obey/perform.inc` |
| `_action_obey_individual_perform` | **strict EXACT** | `BEST_individual_perform.inc` |
| `_action_obey_command_perform` | **strict EXACT** (bytediff 0) | `BEST_command_perform.inc` |
| `_vehicle_possibility_qsort` | **strict EXACT** (via genuine caller) | `qsort_new.inc` |
| `_action_obey_command_begin` | 4336 / 167, frame-packing residual | `BEST_command_begin.inc` |
| `_action_obey_describe_command` | 1824 / 147, cross-jump residual | `describe_v1.inc` (+ dialogue include) |
| `_action_obey_control` | unchanged (register/aggregate-copy tie) | — |
Post-header landing file with no scratch shadows: `scratch/workers/action_obey/LANDING_after_headers.c`.
## 4. Why nothing landed (owner block)
Every new body needs declarations that belong to shared headers (brief §1 forbids worker
header edits and TU-local shadows):
- `describe_command`, `command_begin`: `struct ai_{animation,script,recording}_reference_definition`
(January element sizes 0x3c/0x28/0x28; PDB records).
- `command_begin`/`command_perform`: go_to, look, animation_mode, crouch, grenade, vehicle,
animate, action, targeting, initiative, wait, die modifier domains; actor fire-target and
combat-status domains; unit animation impulses.
- `individual_perform` requires both private bodies; `perform` requires `individual_perform`;
the qsort comparator is emitted only through `command_begin` (static COMDAT law).
- Owner census: the graph also emits SELECT_ANY copies of `distance_squared3d`, `dot_product2d`,
`dot_product3d`, `vector_from_points3d`, `real_random_range`, which January references as
undefined externals (per-caller real_math inline boundary; base already emits the
magnitude/normalize/scale/square_root family).
Exact ORCHESTRATOR-PROPOSAL text, conflict lists and consumer lists:
`scratch/workers/action_obey.md` (end of file) and `scratch/workers/action_obey/prereq.inc`.
## 5. Accepted source controls (evidence per shape)
- **command_perform**: single `done` result returned once (every January epilogue is `mov al,bl`);
each guard path assigns its result explicitly (`else { done = TRUE; }`) — removing any one of
the eight explicit TRUE results changes bytes (measured individually); the TRUE-atom case group is
the last case (its `mov bl,1` block falls into the return block); `done = dot_product3d(...) >
current_command->parameter1` for move_direction; flying/2D facing arms share one 0.984 compare via
`goto facing_done` (HCEA CFG); assert line 983 names `current_command`; action result
`!complex_control || !complex_control->play_action`.
- **individual_perform**: `command_valid = FALSE; if (index < count) command_valid = TRUE;`
(January's branchy stores; `= index < count` emits setl and swaps two byte slots).
- **vehicle_possibility_qsort**: `<` → -1, `>` → 1, else 0 with three returns (January reloads `a`
between compares); HEAD's `a >= b ? a > b : -1` body is the wrong shape.
- **describe_command**: function-scope 5-entry look-modifier table, block-local tables per case
(packed into the same top slots), 11-entry action table, 256-byte teleport buffer (frame 0x140
exact); `dialogue_get_vocalization_name` must be prototyped (`units/dialogue_definitions.h`)
— without it C4013 promotes the short argument with movsx.
- **command_begin**: see §6 v10; ABI EAX=complex_control / ECX=unit_index reproduced.
## 6. Experiment matrix (bounded)
describe_command: v1 all-break 1824/147; v2 all-return identical; v3/v4 mixed return/break
1872/149 and 1824/147; v1b + dialogue include 1824/147 (vocalize fixed). Residual = VC7
cross-jump selection only.
control: single overwritten local (Ghidra form) byte-identical to HEAD; alignment store before
targeted / first → worse; jump-block scope local → identical. Residual = reversed field order of a
register-pair struct copy.
command_perform: v1 1456/52 → v2 goto facing 1424 → v3 HCEA returns 1408/51 (wrong ABI:
complex_control register-passed) → v12 mixed 1408 regalloc right → v14 explicit guard results →
v18 TRUE group last (1 byte) → v19b/v20 EXACT. Uninitialised-`done` probe rejected (UB, and wrong).
individual_perform: donor body 272; A (FALSE/if TRUE) EXACT; ternary next index 272; C 288 sha.
command_begin: v1 4368/179 (switch-compiled sub-dispatch) → v2 if/else chains 4320/167 →
v3 single exit → v5 `short command_number` → v6 two distance locals → v7 double duration store
(refs match January) → v10 scenario local / surface arg / vocalize order / biped ternary 4336/167.
Probes inert or worse: short index local, declaration order, real_point3d facing, direction and
iterator scope moves.
## 7. Do-not-repeat
- describe_command return/break mixes; control statement moves and local scoping; command_begin
declaration-order permutations (density ranking ignores order off ties).
- Never use an uninitialised result, TU-local enum/struct shadows, or non-static externs for the
private pair.
## 8. Residual classification
- control: register/store-order tie (measured).
- describe_command: cross-jump granularity (measured instruction-level; source cause unknown).
- command_begin: frame-allocation packing — January classes {direction, teleport facing,
head_position, vehicle_origin, prop_iterator, actor_definition} (end-aligned at -0x14) and
{object_iterator, data_iterator} at -0x40; ours forms {object_iterator, facing, prop_iterator,
actor_definition}, {direction, head, origin} and a lone data_iterator (+16 bytes). Inference, not proof.
## 9. Reopen criteria
- Header packet A–C (and census decision D) landed → gate `LANDING_after_headers.c`; land the four
exact rows, park or keep begin/describe fuzzy.
- command_begin: an evidence-led function-scope promotion that reproduces January's packing classes.
- describe_command: a source-level reason for January's sparser cross-jumping.
## 10. Disposition
Still active; zero new production credit from this lane. Four rows are strict EXACT in a complete
scratch TU pending owner-header prerequisites.
## Lead admission (2026-09-14): graph landed
The worker's scratch-only prerequisites were resolved as follows, and the
complete private graph was then admitted:
- **Header prerequisite `ef7bc742a`**: `source/ai/ai_scenario_definitions.h` now
owns the command-list atom modifier enums and the ai animation/script/recording
reference definitions (HCEX PDB names; January element sizes 0x3C/0x28/0x28).
`action_alert.c`'s local reference struct was removed. The full build showed
no inherited strict loss; two already-parked fuzzy functions were re-measured
(documented in the park evidence).
- **TU-local enums with disclosure**: the actor fire-target and combat-status
levels cannot be added to `actors.h` in this lane because the active
`actor_perception.c` already defines the combat-status enumerators locally (a
redefinition would break it). They follow canonical's existing TU-local practice
(copies in actors.c, actor_combat.c, actor_perception.c, ai_script.c). The unit
animation impulse enum is TU-local because units.obj is outside this lane
(units.c declares only the count locally).
- **Adversarial review** accepted `_action_obey_command_perform` (1,408),
`_action_obey_individual_perform` (288), `_action_obey_perform` (192) and
`_vehicle_possibility_qsort` (64) as strict exact, and
`_action_obey_describe_command` (1824/147 vs 1936/153) as fuzzy. It rejected
the first `_action_obey_command_begin` spelling for byte-inert house-rule
defects. The lead removed the duplicated `look_duration` store, collapsed the
extra look_player braces, re-indented the command block (whitespace only) and
rewrote the enum provenance comments accurately. The re-gate is unchanged:
24 exact / 3 residual / 0 unwritten, fake scan 0 leads.
- **Disclosed owner census**: header-inline SELECT_ANY COMDAT copies
`_distance_squared3d`, `_dot_product3d`, `_real_random_range`,
`_vector_from_points3d` (January references them as undefined externals) and
an unreferenced `_dot_product2d` copy. This is the same systemic class as the
base object's pre-existing magnitude/normalize/scale/square_root copies, and
no new non-inline code owner was added.
- `_action_obey_command_begin` (4336/167 vs 4320/167) and
`_action_obey_describe_command` land as zero-credit fuzzy genuine callers.
`_action_obey_control` is unchanged.
@@ -1,191 +0,0 @@
# ActionObey partial callback graph reconciliation
Date: 2026-09-05. Parent: `c32247e7651d680dcd133d72b2fbf4e8009cc124`.
## Accepted result
The verified partial graph advances ActionObey from **10/27 to20/27 strict
exact target functions**, **485 to2,436 meaningful code bytes**, and
**528 to2,560 padded bytes**. New credit is **1,951 meaningful /2,032 padded
bytes and10 functions**. Seven target functions remain unwritten; this is not
a completed object.
Canonical totals after the independently checked accounting repair are
**912,298 /2,198,102 meaningful exact code bytes**, **6,197 /11,060 credited
functions**, **2,061,020 /4,176,062 matched data bytes**, and **391/833 Matching
objects**. The separate stable owner sweep advances6,233 to**6,243 /8,245**,
with zero inherited exact losses. All298 tests plus26 subtests pass.
There are284 valid fuzzy parks, zero stale/invalid entries, and no revoked
object admissions.
| New target function | Meaningful bytes | Padded bytes |
| --- | ---: | ---: |
| action_obey_command_end |549|560|
| action_obey_individual_setup |110|112|
| action_obey_individual_flush_command_indices |49|64|
| action_obey_individual_begin |71|80|
| action_obey_individual_advance |20|32|
| action_obey_individual_end |72|80|
| action_obey_individuals_iterate |290|304|
| action_obey_command_list_setup |383|384|
| action_obey_directmovement_update_facing |336|336|
| action_obey_individual_update |71|80|
## Genuine source and private caller closure
Only `source/ai/action_obey.c` changes implementation. No shared header
from the failed command-owner trial is retained. Public prototypes remain
in the existing ACTION_OBEY owner section of `actions.h`; all nine newly
implemented private functions are static with semantic names. The matching
nine `config/symbols.json` records are edited in place, preserving the
23,225-record order and every other record.
The public setup routine and five existing public wrappers retain the real
iterator/callback graph. The end callback retains command_end; the update
callback retains directmovement_update_facing. No dummy caller, emission
anchor, forced inline, private extern ABI, or consumer-local owner declaration
is introduced. The excluded private begin/perform pair and its individual
perform caller are not declared as fake externs.
The source consumes the already accepted full swarm/obey/unit/biped records,
typed datum/object/tag access, project boolean/real types and flag macros,
named command cases, original assertions and PC-only loop diagnostics.
Parameters/prototypes retain the house format and explicit returns.
The structure-cache leaf is unchanged.
The real math owner supplies cross_product3d and negate_vector3d through the
new facing caller. Their two include-time aliases and manual duplicate
bodies are removed atomically. Both existing target bodies remain strict
exact. Their selection metadata changes from manually emitted NODUPLICATES
to natural header SELECT_ANY; that ownership difference is disclosed, not
mistaken for identical object metadata. The other two inherited manual
aliases, random_range and vector_from_points2d, are unchanged pending their
genuine private-command callers. This partial packet does not endorse them
as final source topology.
## Provenance adjudication
Fable's preserved donor is
`C:/halo-worktrees/fable-50k-small-families-20260904`, commit
`f9dd42218b3325e1b3a63d7860e5309f69fd3d8e`; it remains read-only.
These are authenticated reconstructions, **not a claim that all Fable bodies
were first-shot natural matches**. Its `scratch/reports/action_obey.md`
records an earlier early-return setup shape changed to the ordinary single
success-result form, a command_end case ordering correction to January's
physical case-block order, and a signed-short swarm flag correction.
The signed field is now independently PDB-proven. The retained success-result
control flow is meaningful, follows the engine's common single-result
style, and matches January's state-store/dataflow. The command case order
corresponds to the actual January layout without changing behavior or adding
nonsensical control flow. No spelling, lifetime, declaration-order, flags,
assembly, or source-shape search was conducted during this integration.
This adjudication does not reopen exhausted tuning recipes elsewhere.
## Accounting repair, not a relaxed match
Ordinary objdiff reports command_end at85.26786% and the facing helper
at96.77419%, even though complete code and ordered relocation checks pass.
Two narrow semantic ledger entries restore their549+336 meaningful bytes:
- command_end:560 bytes,24 relocations, normalized SHA-256
`f59f0185a672ce6409fe3d3c3a439e517483593f6ffc9bf5eff4b18dd4420a61`;
- facing:336 bytes,17 relocations, normalized SHA-256
`17b28fabb3ad4366074689d2f9be1364ccf07cdc0c7a185dbd4dff25b00e3f97`.
The discrepancy is compiler-local switch-label representation. Every build
must still pass the unchanged strict comparator; no target bytes, branch
destinations, relocation identities, or comparison policy were altered.
Without these ledger entries the report showed911,413 bytes and6,195
functions. The additional885 bytes are part of this same1,951-byte packet,
not another reconstruction gain.
## Whole-object boundary
The candidate naturally emits five math functions absent from this
ActionObey target: square_root16, scale_vector3d48, magnitude_squared3d48,
magnitude3d48 and normalize3d96 padded bytes. All are SELECT_ANY. Only
normalize3d has incoming candidate relocations, from the facing function at
167 and217; the other four are header emission. The full572-base/833-target
owner census finds byte/relocation-identical available implementations for
each helper. **None receives ActionObey target credit.**
Code equality is not link compatibility. Ordinary frozen actual base/base
links with ActionCharge fail in both input orders with LNK2005 for
square_root, scale_vector3d, magnitude3d and normalize3d: ActionCharge owns
NODUPLICATES copies. Both links also retain37 unrelated unresolved externals
and produce no final binary. No FORCE, IGNORE, stub, object patch, selection
patch, or synthetic discard pass was used. Full Ninja success means
compilation/reporting, not successful final-image linkage.
An explicit ActionObey whole-object rejection records this ownership boundary
and the remaining inherited alias topology. Target-function progress remains
valid separately. Reopening requires authentic source/owner evidence and
ordinary compatible canonical linkage, not manual expansion or forced
inlining to hide helper emission.
## Verification evidence
All paths below are relative to canonical:
`C:/Users/isabe/Documents/Codex/2026-07-13/i-w/work/rasterizer-model-wrapper-alias-20260829`.
- Frozen `scratch/action-obey-partial-graph-before-20260905.json` and
`*-after-20260905.json` include source, target/base objects, symbol manifests,
and hashes of all572 base objects. Only ActionObey changes; all571 other
complete base-object hashes are identical.
- Whole frozen base hashes: before
`5335c76a7e9833303b69e3a2d3a8e71fb73ae34e2b83aac4d22642a11c6659d6`;
after `f339f7776b58f5d044015dc363a709ac649878c918a61591a6e21a71ebedb4f9`.
Renamed January target:
`13a4e47de2c79a3122033539284d148cae052f5f008f084a3d15862dda844368`.
- `scratch/action-obey-partial-graph-stable-after-20260905.json`:
ten gains/2,032 padded bytes, zero losses.
- Gate:20 exact, zero residual, seven unwritten; no emitted
`_point_from_line3d`. No candidate code/data/BSS/COMMON owner is silently
excluded from the complete-object inventory.
- Independent object audit root replay:
`scratch/action-obey-partial-graph-independent-object-root-20260905.json`,
SHA-256 `321305aa1268865dfc91d598ab827ab24de37a3e9e1bfcd99880a7e3eb71c104`.
It verifies exact positional symbol edits, all sections and all new owners.
- Math-owner census root replay:
`scratch/action-obey-partial-graph-math-comdat-root-20260905.json`,
SHA-256 `6f236108159648eccef28a68c9c07e221abeedf76c268a30c093c971aaf586b6`.
- Ordinary links:
`scratch/action-obey-partial-graph-ordinary-links-20260905.json`,
SHA-256 `8e0a4d74b64caae7d1867b47a1d5e50cfdf1993f260746760190eaf7eefd0ce5`.
- Public verification tool:
`tools/audit/action_obey_callbacks_runtime_differential.py`,
SHA-256 `1caf0250cc996639dd792145894cfbd381fc61546c380f4d625e7b6c18833697`.
Its target-first cases cover setup9, flush44, advance256, in target,
actual and isolated objects. Real six-argument callback ABI, complete
modeled0x24/0x58 state, API arguments/order, caller stack frame, ESP,
nonvolatile registers and DF all pass. Setup's authentic null-reference
assertion/noreturn exit is covered. Tag indices/handles stay within the
documented valid model; this is not a whole-game execution claim.
- `tools/test_action_obey_callbacks_runtime_differential.py` adds ten
domain/negative-control tests. Full `tools` suite:298+26 pass.
- Final accounted build:
`scratch/action-obey-partial-graph-accounted-build-20260905.log`.
Parks/admission reports have no stale park or revocation.
- Units remains189/189 exact. Both protected bitmap source hashes are
unchanged.
Reproduction of the bounded public oracle:
```text
python -B tools/audit/action_obey_callbacks_runtime_differential.py --actual build/base/source/ai/action_obey.obj
```
The tool requires its documented frozen target and retained HCEA donor
evidence (or explicit paths), and refuses changed section/hash evidence.
## Remaining reconciliation
The coherent larger command-owner trial was rejected after a Units
regression and fully restored; see
`action_obey_obj_command_owner_trial_rejected_20260905.md`.
Private begin/perform source proposals and corrected PDB prerequisite evidence
remain scratch-only, uncompiled, and uncredited. Seven target functions,
remaining data, helper ownership, and two inherited aliases still prevent
ActionObey completion. Fable Bitmaps and other lane packets remain separate
pending work; historical lane totals are not added to canonical.
@@ -1,142 +0,0 @@
# Fable `action_obey` private-closure intake (2026-09-05)
Read-only/source-plan audit. No production, donor, build, configuration, or generated-object file was changed or compiled. This note does not change the earlier intake accounting: the initial Fable report started at 9 exact / 0 residual / 18 unwritten, and the reported donor `+11` remains donor evidence rather than admitted campaign progress. This audit admits zero functions.
## Conclusion
The two missing routines can be restored as a coherent, genuine same-translation-unit closure:
```c
static boolean action_obey_command_begin(
long actor_index,
long unit_index,
short command_list_index,
struct obey_individual_simple_control *simple_control,
struct obey_individual_complex_control *complex_control);
static boolean action_obey_command_perform(
long actor_index,
long unit_index,
short command_list_index,
struct obey_individual_simple_control *simple_control,
struct obey_individual_complex_control *complex_control);
```
January proves that these are private register-ABI functions, not public ABI. `_code_000060c0` receives the complex-control pointer in EAX and unit index in ECX; actor index, command-list index, and simple-control pointer remain stack arguments. `_code_000071a0` receives unit index in EAX and its other four arguments on the stack. Those allocations are consistent with VC7 optimizing the ordinary source signature above after it sees the same-TU callers. The audit aliases should name the private sections, but production source must not declare either body non-static or manufacture an external relocation.
Once both bodies exist, the retained `action_obey_individual_perform` has a real private callee closure and can continue to support public `action_obey_perform`. There is no missing third private body behind either routine. The remaining preconditions are authentic shared schema and owner prototypes, not an ABI invention.
## Primary evidence
- January PC object: `build/split/source/ai/action_obey.obj`, SHA-256 `0e4f889a35b97c44f6ae91e4906b547bd64f2ff12b9f399e02b46f02b69633fb`.
- `_code_000060c0`: 4,320 padded bytes, 167 relocations, normalized SHA-256 `5b56fdd8b88a4456f893740a2f972a21d114b032c636f6b90c9d86e0114fd640`.
- `_code_000071a0`: 1,408 padded bytes, 51 relocations, normalized SHA-256 `afc109dc8ddf7e3f4ee6ed60f606b1e597702f063a81488fb2f8fd24f3713b07`.
- Semantic donor, read in full:
- `research-cache/halocea-full-blobs-20260830/src/blam/ai/action_obey_command_begin.c`, SHA-256 `4373d536adedb3dbdadfeaf62cfd1369ba2d1c53e92acae8477b6837a0a9461f`.
- `research-cache/halocea-full-blobs-20260830/src/blam/ai/action_obey_command_perform.c`, SHA-256 `bd22ee99d261de2f8b1db20ca548bb2158031a7e0d907bdfd78a9813842bd531`.
- `research-cache/halocea-full-blobs-20260830/src/blam/ai/action_obey_individual_perform.c`, SHA-256 `05d10cbc643b6b1b3eee5fe725bb257188a9521ea09df5deeb1370775532dade`.
- `research-cache/halocea-full-blobs-20260830/src/blam/ai/action_obey_directmovement_update_facing.c`, SHA-256 `3295935a5a1602fe4cdbfba17bf2762734699ef1e1bc981aae5c346a6bdcb042`.
- Retained PC source is not a full donor: Stian's `src/halo/ai/action_obey.c` is zero bytes; Punpckhdq retains the January symbol/literal manifest only; Pastudan history commit `ba72a7d7` contains only a raw-float `cross_product3d` reconstruction. It independently corroborates that math operation, but supplies neither private body.
- Fable source reviewed: `C:/halo-worktrees/fable-50k-small-families-20260904/source/ai/action_obey.c`, SHA-256 `067b4900d5494c5341ca609337f0d220dedd4f95778a935d601427de96575a59`.
HCEA is an Xbox/PPC semantic donor. January PC disassembly, relocation destinations, literals, assertion strings, record strides, and register/stack use take precedence wherever the two diverge.
## Natural `command_begin` source plan
Start by resolving the typed actor, command list, current command, point block, actor definition, and actor variant. Return false if `current_command_index` is outside the command count. Dispatch on the 28 `ai_atom_type` values and use the existing simple/complex control fields:
1. `pause`: convert seconds to ticks (`parameter1 * 30`).
2. `go_to` / `go_to_and_face`: validate point indices; set destination point/surface/keep-moving state; call `actor_move_to_point`; optionally call `actor_move_keep_moving_past_destination`; for the facing form, set the typed facing point.
3. `move_direction`: sample actor body position or `object_get_origin`; for a point command call `vector_from_points3d` then `normalize3d`, otherwise use `vector3d_from_angle`; set the facing mode, halt the actor when it owns the unit, and set direct-movement flags.
4. `look`, `look_random`, `look_player`, `look_object`: resolve a command point, active prop, nearest player, or named object; use `random_range(low, high + 1)` and `real_random_range` for the random look; construct a complete local `direction_specification` directly and call `actor_look_secondary`; store the duration in ticks. Do **not** import HCEA's one-argument/variadic `direction_get_empty` declaration: January has no such relocation and writes the local type/object/point fields directly.
5. `animation_mode`, `crouch`, `shoot`, `grenade`: validate domains and populate complex control. Grenade setup takes the grenade type from the actor variant, calls `unit_add_grenade_type_to_inventory`, and initializes trajectory/target/timer state.
6. `vehicle`: iterate `_object_mask_vehicle`, keep at most 16 typed `vehicle_possibility` records, compute distance, `qsort` with the already genuine same-TU `vehicle_possibility_qsort`, and try candidates through `actor_action_try_to_enter_vehicle` using the ordinary empty string literal. The record is TU-private; no public header is owed.
7. `running_jump`, `targeted_jump`: reject the actor's mounted unit case; set jump bits/timers/velocity. January uses `dot_product3d` for the non-actor unit's facing/velocity test.
8. `script`, `animate`, `recording`: call `hs_wake_by_name`; resolve the animation reference/default object animation graph and call `unit_start_user_animation`; update typed biped runtime flags; or resolve/play a recorded animation.
9. `action`, `vocalize`: map modifiers to animation impulses/communication types, or fill a real `unit_speech_item`, initialize its `ai_information_packet`, and call `unit_speak`.
10. `targeting`, `initiative`, `wait`, `loop`, `die`, `move_immediate`, `set_radius`, `teleport`: update the named flag/state fields. Teleport filters with `_object_mask_biped`, reads `biped_definition.biped.flags`, uses full 3D facing for flying/climb-anything bipeds and a typed 2D facing otherwise, then calls `object_set_position`, `object_reset`, `object_compute_node_matrices_recursive`, `actor_input_sample_position`, and `actor_move_halt`.
The PC-only debug epilogue must also be retained. When `ai_debug` is enabled, January formats `<no encounter>` or `encounter/squad`, calls `action_obey_describe_command(..., temporary, 256)`, and reports `"%s: %s #%d%s: %s"` with either `""` or `" FAILED"`. HCEA returns immediately after the switch and does not contain this epilogue.
PC-shape corrections relative to HCEA include ordinary `random_range` / `real_random_range`, `vector_from_points3d`, `distance_squared3d`, and `dot_product3d` calls; direct construction of the look-direction record; the PC debug epilogue; the January empty-string literal; and typed biped/object/tag access. HCEA's seed calls, manually expanded vector/distance products, `nullptr`, raw object header casts, and its `direction_get_empty` facade are semantic hints only.
## Natural `command_perform` source plan
Resolve the typed command and actor, default `done` to true, and use the January switch grouping:
- `pause` and all look atoms, plus `move_immediate`: finish when `pause_timer == 0`.
- `go_to` / `go_to_and_face`: for the actor's own unit with complex control, test `actor_path_at_destination`; apply keep-moving tolerance/velocity logic; wait ten ticks while the actor is still moving; when destination facing is active, assert at source line 983 that the current atom is `_ai_atom_go_to_and_face`, normalize a 3D or 2D vector to the facing point, compare against `0.98400003f`, then halt on completion.
- `move_direction`: obtain actor body position or object origin, form a vector from the stored start position, and compare its dot product with the commanded travel distance.
- Immediate atoms (`animation_mode`, `crouch`, `vehicle`, `script`, `targeting`, `initiative`, `loop`, `die`, `set_radius`, `teleport`) return true.
- `shoot`: require the actor unit and complex control; compare the manual fire target with the ordered point (`distance_squared3d < 0.25f`); otherwise set pause from `actor_variant_definition.ranged_combat.first_burst_delay_upper_bound * 30`, floored at 60.
- `grenade`: inspect `unit.grenade_throw_state` after start, or call `actor_combat_plan_grenade_trajectory` and depress the trigger when the unit is not busy.
- Jump atoms: require the jump flag, use actor `input.in_midair` or `unit_flying_through_air`, and clear the timer after the jumped bit and airborne state coincide.
- `animate`, `recording`, `action`, `vocalize`: inspect `unit.animation.state`, `recorded_animation_controlling_unit`, `complex_control->play_action`, or `unit.speech.current.priority` respectively.
- `wait`: preserve the January three-way combat-status/told-to-advance ladder.
Use `vector_from_points3d`, `distance_squared3d`, `magnitude_squared3d`, `dot_product3d`, `normalize3d`, and `normalize2d` as the typed math vocabulary instead of HCEA's manually expanded products. January's relocations authenticate every non-inlined call.
## Inline-math ownership and true callers
The four currently exact out-of-line bodies are authenticated `source/math/real_math.h` inline operations, but the present macro-renaming plus hand-appended definitions are a temporary facade. Preserve their current exact credit until the real callers are present, then remove the four include-time renames and four manual definitions atomically and let ordinary header ownership materialize them. A normal compile must prove all four sections remain exact before that cleanup is retained.
| Target section | January | True source caller | PC evidence |
| --- | --- | --- | --- |
| `_random_range` | 32 bytes, 2 relocs | `action_obey_command_begin`, `look_random` | call relocation at private-body `+0x25f`; body calls `get_global_random_seed_address` and `seed_random_range` |
| `_vector_from_points2d` | 32 bytes, 0 relocs | `action_obey_command_perform`, non-flying destination-facing branch | the helper is inlined into `+0x211..+0x22e`: two point-coordinate subtractions followed by `normalize2d`; emitted wrapper plus inline sequence identifies the reference |
| `_cross_product3d` | 64 bytes, 0 relocs | `action_obey_directmovement_update_facing` | two direct call relocations at `+0x9e` and `+0xd0` |
| `_negate_vector3d` | 48 bytes, 0 relocs | `action_obey_directmovement_update_facing` | backwards/right cases are inlined at `+0x72..+0x88` and `+0x123..+0x138` |
For the 2D facing call, use a genuinely typed `real_point2d`/`real_vector2d` projection or authentic shared view. Do not recover it with a pointer cast between unrelated point/vector types, an inactive-union read, or a prefix facade merely to provoke the wrapper.
## Owner headers and API closure
Most dependencies already have the correct owner declarations and should be consumed by includes, not redeclared in `action_obey.c`:
| Owner | Required declarations/types |
| --- | --- |
| `source/ai/actions.h` | shared obey simple/complex/state types; `actor_action_try_to_enter_vehicle`; public ActionObey declarations |
| `source/ai/actors.h` | `actor_datum`, `direction_specification`, movement/look/path/grenade APIs, `actor_input_sample_position` |
| `source/ai/actor_definitions.h` | actor/variant tags, idle-look range, ranged first-burst delay, grenade properties |
| `source/ai/ai_scenario_definitions.h` | command-list/command/point records and all atom/command-list/modifier domains |
| `source/ai/props.h` | `prop_iterator`, `prop_iterator_new/next`, `prop_get_active_by_unit_index` |
| `source/ai/ai_communication.h` and `source/ai/ai.h` | communication enums and complete `ai_information_packet` |
| `source/game/players.h`, `source/memory/data.h` | `player_data`, `player_datum`, typed `data_iterator_new/next` |
| `source/objects/objects.h`, `source/objects/object_definitions.h` | object iterators/accessors/origin/name/position/reset/node matrices, masks, damage flags, animation graph tag |
| `source/units/units.h`, `source/units/bipeds.h`, `source/units/biped_definitions.h` | unit speech/animation/grenade/facing APIs and complete unit/biped runtime/tag fields |
| `source/scenario/scenario.h`, `source/scenario/scenario_definitions.h` | `global_scenario_get`, command/reference blocks, encounter/squad debug names |
| `source/cutscene/recorded_animation_definitions.h`, `source/cutscene/recorded_animations.h` | scenario animation lookup and recorded-animation control/playback |
| `source/hs/hs.h` | `hs_wake_by_name` |
| `source/math/real_math.h` | all point/vector/random helpers and constants |
| `<stdlib.h>` plus project cseries/error headers | `qsort`, memset/string/format/error/debug support |
Two genuine owner-prototype gaps were found:
1. `actor_move_keep_moving_past_destination(long)` is defined by `source/ai/actor_moving.c` but is absent from `source/ai/actors.h`; `source/ai/action_charge.c` currently carries a foreign C-local declaration. Add the owner prototype to `actors.h` and remove consumer-local copies.
2. `recorded_animation_play(long, short)` is defined by `source/cutscene/recorded_animations.c` but is absent from `source/cutscene/recorded_animations.h`; `source/hs/hs.c` currently declares it locally (with a `word` second parameter). Add the definition-compatible owner prototype to `recorded_animations.h` and remove/repair foreign copies.
No `direction_get_empty` declaration should be added: it is not a January dependency. `vehicle_possibility_qsort`, `action_obey_directmovement_update_facing`, `action_obey_command_begin`, and `action_obey_command_perform` remain genuine statics in this TU. `qsort` comes from the standard header, not a handwritten extern.
## Shared schema/enum prerequisites
Before integration, the owning headers must supply one authentic definition for:
- `obey_individual_simple_control` (0x24), `obey_individual_complex_control` (0x58), and `obey_state_data` (0x84), including direct-movement/jump union arms and every destination/action/shoot/grenade field;
- the complete shared actor/swarm layouts already being audited separately; no obey-only prefix, opaque tail, or parallel incompatible record is acceptable;
- `direction_specification`, `unit_speech_item`, `ai_information_packet`, `vehicle_possibility` (TU-private), and the command/reference records with January strides: command list 0x60, command 0x20, point 0x14;
- the command-list flags, simple-control bits, atom modifier domains (look, move facing, grenade, animate, action, loop), unit animation impulses, biped runtime flags, and existing biped-definition/communication/speech/object domains in their natural owners.
Current Fable-local enums explicitly labeled “belongs in ...” should migrate to those owners with the full packet; they are not a reason to create another private copy. The exact signedness of the shared swarm component flags remains part of the separate schema closure and must not be inferred from this pair of functions.
## Admissible packet boundary
A coherent implementation packet is:
1. land the authentic shared schema and the two owner-prototype fixes;
2. include the actual owners;
3. add both private static bodies together, including the PC-only assertion/debug behavior and ordinary typed accesses;
4. keep `action_obey_individual_perform` calling those statics and remove the two non-static placeholder declarations;
5. reconcile the four math wrappers only after their true calls are present, in the same verified packet;
6. add audit-only aliases from the two authentic names to `_code_000060c0` / `_code_000071a0` for measurement.
This packet is source/API/schema feasible and does not require an external facade. `command_perform` is the smaller, higher-confidence body; `command_begin` is large but semantically closed. Neither should be admitted alone if the goal is to retain public perform without unresolved private ABI.
@@ -1,455 +0,0 @@
# ActionObey begin/perform owner prerequisites (read-only audit, 2026-09-05)
## Scope and disposition
This is the complete owner-prerequisite inventory for the frozen private
`action_obey_command_begin` and `action_obey_command_perform` source
proposals. The published baseline inspected was
`c32247e7651d680dcd133d72b2fbf4e8009cc124`; concurrent root work after that
checkpoint is outside this audit. No production source, header, build
configuration, target metadata, gate, or object was changed here.
The two source proposals remain research, not admitted code:
- `scratch/action_obey_command_begin_proposal_20260905.c`, SHA-256
`630ee045a17f3a095929516cd524c1ee40a385eb08d75a2b133ed99ebcca5ead`;
- `scratch/action_obey_command_perform_proposal_20260905.c`, whose final hash
is recorded at the end of this ledger. The earlier performer SHA-256
`23cb0320a7b64abb4111cb3269983f50b5ff3ca7287edb455349d04a9b737ce3`
is retained as history: it used three unauthenticated `until_...` wait
names and incorrectly assigned an adjacent `"always"` literal to the wait
domain. The corrected proposal uses the direct PDB names and preserves
January's out-of-domain wait result of `TRUE`.
Root already exercised one coherent 86-consumer owner-header trial from the
6,233-strict checkpoint. It lost the existing strict
`Units::_unit_preprocess_node_orientations` owner (6,233 -> 6,232) and was
fully restored without a name/order/source-shape retry. The frozen evidence
is:
- `scratch/action-obey-command-owners-before-20260905.json`, SHA-256
`34c077c0b3cbe43476e5e9dadc1a24fc22d29c462db23976b8ae1b8a45d8702d`;
- `scratch/action-obey-command-owners-after-20260905.json`, SHA-256
`090874e7e335ae137a823e4de032de6122ca89bee8860bfd397fec166a08a404`;
- `scratch/action-obey-command-owners-restored-20260905.json`, SHA-256
`23e6ff746d10fa75f5c23449f16903138281cac98ca62b54da20053adcd4523a`;
- stable before/restored reports, both SHA-256
`f714aec1804b522fbe405ea393c82a21c79569f20a19a365a43a47a77b662642`.
Consequently, this ledger is an actionable future boundary, not a request to
repeat that failed trial. The begin source itself remains frozen even where
this audit identifies better owner-constant spellings.
## Completeness verdict
The two fragments have exactly these missing shared prerequisites:
1. two complete actor constant domains in `source/ai/actors.h`;
2. twelve complete command-modifier domains and three complete scenario AI
reference records in `source/ai/ai_scenario_definitions.h`;
3. the complete 0..13 unit-animation-impulse domain in
`source/units/units.h`;
4. removal of the one current scenario-record shadow in
`source/ai/action_alert.c` and the enumerator shadows listed below; and
5. direct inclusion of `units/unit_definitions.h` and
`cutscene/recorded_animations.h` by `action_obey.c` when the begin body is
integrated.
No additional public API is missing. The private begin/perform functions,
`vehicle_possibility_qsort`, and
`action_obey_directmovement_update_facing` remain same-TU `static`
declarations. `vehicle_possibility` remains a genuine private 8-byte record;
it is not a shared-header type. The runtime vehicle-seat-desire enum is not
part of this packet: command validation uses the separate five-value
`ai_atom_vehicle_modifier` domain and forwards its short value through the
already-owned actor API.
## Exact actor-owner declarations
`source/ai/actors.h` is the genuine owner. DIA reports both domains as
`enum <unnamed-tag>`, so use anonymous blocks and leave every datum backing
field as `short`:
```c
enum
{
_actor_fire_target_none = 0,
_actor_fire_target_prop = 1,
_actor_fire_target_manual_point = 2,
number_of_actor_fire_targets = 3,
};
enum
{
_actor_combat_status_none = 0,
_actor_combat_status_wary = 1,
_actor_combat_status_investigate = 2,
_actor_combat_status_definite = 3,
_actor_combat_status_certain = 4,
_actor_combat_status_clear_los = 5,
_actor_combat_status_dangerous = 6,
_actor_combat_status_visible = 7,
NUMBER_OF_ACTOR_COMBAT_STATUS_LEVELS = 8,
};
```
Direct `HCEX_Release.pdb -sym` results authenticate every value enumerator
and number. The lower-case `number_of_actor_fire_targets` spelling is the
database spelling; an upper-case replacement is not authenticated. The
terminal counts are domain/database corroboration rather than separately
emitted PDB constants. `actor_control_data.current_fire_target_type` at
`+0x1A0` and the actor-state combat-status fields remain `short`; declaring a
four-byte C enum field would corrupt the authenticated layout.
Conflicting current declarations to remove in a coherent future packet:
- fire-target `none`/`prop` from the mixed blocks in
`source/ai/actor_combat.c` and `source/ai/actors.c`;
- the full combat-status block in `source/ai/actors.c`;
- the partial combat-status blocks in `source/ai/actor_stimulus.c`,
`source/ai/actor_type_infection.c`, and `source/ai/actor_type_flood.c`;
- only `_actor_combat_status_certain` from the mixed anonymous block in
`source/ai/ai_communication.c` (the following
`_ai_information_none = 0` remains an explicit reset); and
- the differently named sole alias
`_action_charge_combat_status_clear_line_of_sight = 5` in
`source/ai/action_charge.c`, changing its one consumer to
`_actor_combat_status_clear_los`.
The complete conflict/use census and direct offset proof are preserved in
`scratch/action_obey_actor_enum_owner_packet_20260905.md`.
## Exact command-modifier owner declarations
All of these belong in `source/ai/ai_scenario_definitions.h`. Use anonymous
blocks: DIA exposes the source domains as anonymous enums, and command
storage remains `short atom_modifier`.
```c
enum
{
_ai_atom_go_to_modifier_stop_at_point = 0,
_ai_atom_go_to_modifier_keep_moving = 1,
NUMBER_OF_AI_ATOM_GO_TO_MODIFIERS = 2,
};
enum
{
_ai_atom_look_modifier_idle_aim = 0,
_ai_atom_look_modifier_idle_turn_around = 1,
_ai_atom_look_modifier_idle_look = 2,
_ai_atom_look_modifier_force_facing = 3,
_ai_atom_look_modifier_force_aim_weapon = 4,
NUMBER_OF_AI_ATOM_LOOK_MODIFIERS = 5,
};
enum
{
_ai_atom_animation_mode_modifier_noncombat = 0,
_ai_atom_animation_mode_modifier_asleep = 1,
_ai_atom_animation_mode_modifier_combat = 2,
_ai_atom_animation_mode_modifier_panic = 3,
NUMBER_OF_AI_ATOM_ANIMATION_MODE_MODIFIERS = 4,
};
enum
{
_ai_atom_crouch_modifier_disable = 0,
_ai_atom_crouch_modifier_enable = 1,
NUMBER_OF_AI_ATOM_CROUCH_MODIFIERS = 2,
};
enum
{
_actor_atom_grenade_modifier_toss = 0,
_actor_atom_grenade_modifier_lob = 1,
_actor_atom_grenade_modifier_bounce = 2,
NUMBER_OF_AI_ATOM_GRENADE_MODIFIERS = 3,
};
enum
{
_ai_atom_vehicle_modifier_any_non_driver = 0,
_ai_atom_vehicle_modifier_gunner = 1,
_ai_atom_vehicle_modifier_passenger = 2,
_ai_atom_vehicle_modifier_driver = 3,
_ai_atom_vehicle_modifier_any_seat = 4,
NUMBER_OF_AI_ATOM_VEHICLE_MODIFIERS = 5,
};
enum
{
_ai_atom_animate_modifier_relative_movement = 0,
_ai_atom_animate_modifier_absolute_movement = 1,
_ai_atom_animate_modifier_absolute_movement_no_collision = 2,
_ai_atom_animate_modifier_no_interpolation_relative_movement = 3,
_ai_atom_animate_modifier_no_interpolation_absolute_movement = 4,
_ai_atom_animate_modifier_no_interpolation_absolute_movement_no_collision = 5,
NUMBER_OF_AI_ATOM_ANIMATE_MODIFIERS = 6,
};
enum
{
_ai_atom_action_modifier_berserk = 0,
_ai_atom_action_modifier_surprise_front = 1,
_ai_atom_action_modifier_surprise_back = 2,
_ai_atom_action_modifier_evade_left = 3,
_ai_atom_action_modifier_evade_right = 4,
_ai_atom_action_modifier_dive_forward = 5,
_ai_atom_action_modifier_dive_back = 6,
_ai_atom_action_modifier_dive_left = 7,
_ai_atom_action_modifier_dive_right = 8,
_ai_atom_action_modifier_vehicle_woohoo = 9,
_ai_atom_action_modifier_vehicle_scared = 10,
NUMBER_OF_AI_ATOM_ACTION_MODIFIERS = 11,
};
enum
{
_ai_atom_targeting_modifier_enable = 0,
_ai_atom_targeting_modifier_disable = 1,
NUMBER_OF_AI_ATOM_TARGETING_MODIFIERS = 2,
};
enum
{
_ai_atom_initiative_modifier_enable = 0,
_ai_atom_initiative_modifier_disable = 1,
NUMBER_OF_AI_ATOM_INITIATIVE_MODIFIERS = 2,
};
enum
{
_ai_atom_wait_modifier_alerted = 0,
_ai_atom_wait_modifier_visible_enemy = 1,
_ai_atom_wait_modifier_told_to_advance = 2,
NUMBER_OF_AI_ATOM_WAIT_MODIFIERS = 3,
};
enum
{
_ai_atom_die_modifier_normal = 0,
_ai_atom_die_modifier_silent = 1,
NUMBER_OF_AI_ATOM_DIE_MODIFIERS = 2,
};
```
The PDB directly authenticates the go-to values, all three wait values, and
the exact value names used by begin for crouch enable, targeting enable,
initiative enable, and silent death. It also authenticates look values 1..4
and every animate/action value. The retained database headers below
authenticate the complete look domain (including idle-aim 0), animation-mode,
grenade, vehicle, animate, and action domains. Paired opposite values and
terminal counts for the two-valued domains, plus the go-to/wait counts, are
complete-domain corroboration from the retained Demon header; do not describe
those counts as separately emitted PDB symbols.
There is no wait `always` member. The adjacent January `"always"` literal is
`_ai_atom_loop_modifier_always = 0`; the already-owned loop value
`_ai_atom_loop_modifier_until_told_to_advance = 1` remains unchanged. The
already-admitted reconstructed move-facing block also remains unchanged and
is not another prerequisite domain.
The frozen begin proposal still spells five comparisons numerically. A
future semantic application should use the genuine names without treating
that as a source-shape experiment:
- go-to `== 1` -> `_ai_atom_go_to_modifier_keep_moving`;
- crouch `== 1` -> `_ai_atom_crouch_modifier_enable`;
- targeting `== 0` -> `_ai_atom_targeting_modifier_enable`;
- initiative `== 0` -> `_ai_atom_initiative_modifier_enable`;
- die `== 1` -> `_ai_atom_die_modifier_silent`.
No exact-name local shadows of these twelve domains exist in canonical
source. The present `ai_scenario_definitions.h` loop and move-facing blocks
must not be duplicated.
## Exact scenario-reference records
These are complete tag-block element records and belong in
`source/ai/ai_scenario_definitions.h`, adjacent to the other command-list
schema. Direct `HCEX_Release.pdb -type` output authenticates every field
name, field type, offset, and total size; none of the fields below is a
speculative tail name.
```c
struct ai_animation_reference_definition
{
char animation_name[TAG_STRING_LENGTH + 1];
struct tag_reference animation_graph;
unsigned long unused[3];
};
struct ai_script_reference_definition
{
char script_name[TAG_STRING_LENGTH + 1];
unsigned long unused[2];
};
struct ai_recording_reference_definition
{
char recording_name[TAG_STRING_LENGTH + 1];
unsigned long unused[2];
};
typedef char ai_animation_reference_definition_size_assert[
sizeof(struct ai_animation_reference_definition) == 0x3C ? 1 : -1];
typedef char ai_script_reference_definition_size_assert[
sizeof(struct ai_script_reference_definition) == 0x28 ? 1 : -1];
typedef char ai_recording_reference_definition_size_assert[
sizeof(struct ai_recording_reference_definition) == 0x28 ? 1 : -1];
```
The exact offsets are animation name `+0`, tag reference `+0x20`, unused
dwords `+0x30`; script/recording name `+0`, unused dwords `+0x20`.
The only current declaration conflict is the complete local
`struct ai_animation_reference_definition` at
`source/ai/action_alert.c:76`. It must be removed so that the already-present
`#include "ai_scenario_definitions.h"` supplies the owner. Its local
`long unused[3]` has the right width but the PDB owner type is
`unsigned long`. There is no current script- or recording-reference shadow.
The `scenario` structure's corresponding members remain `struct tag_block`;
no scenario prefix or retyping is required.
## Exact unit-animation-impulse owner declaration
`source/units/units.h` is the genuine public constant owner. Again use an
anonymous enum and keep the existing public function parameters as `long`
and the private mapper's parameters as `short`:
```c
enum
{
_unit_animation_impulse_berserk = 0,
_unit_animation_impulse_signal_move = 1,
_unit_animation_impulse_signal_attack = 2,
_unit_animation_impulse_signal_warn = 3,
_unit_animation_impulse_surprise_front = 4,
_unit_animation_impulse_surprise_back = 5,
_unit_animation_impulse_evade_left = 6,
_unit_animation_impulse_evade_right = 7,
_unit_animation_impulse_dive_front = 8,
_unit_animation_impulse_dive_back = 9,
_unit_animation_impulse_dive_left = 10,
_unit_animation_impulse_dive_right = 11,
_unit_animation_impulse_vehicle_celebrate = 12,
_unit_animation_impulse_vehicle_panic = 13,
NUMBER_OF_UNIT_ANIMATION_IMPULSES = 14,
};
```
Direct `HCEX_Release.pdb -sym` queries return every value enumerator with
exactly these names and values. The terminal count 14 is supported by the
complete January range/switch and database domain, not a separately emitted
PDB constant. The immutable HCEA header's older `GUESS` prose predates this
direct PDB check and does not downgrade the now-authenticated value names.
Current canonical has no local value-name shadow. Remove only
`NUMBER_OF_UNIT_ANIMATION_IMPULSES = 14` from the mixed block near
`source/units/units.c:708`; retain `_unit_seat_unknown8_bit = 8`.
For a complete semantic migration, replace the raw 0..13 cases in both
switches of private `code_00198e40` and the raw 12..13 range tests in private
`code_00198fd0` with these owner constants. The PDB authenticates
`code_00198e40` as
`static short unit_animation_impulse_get_index(short, short *)`; that private
symbol rename is useful metadata cleanup but is not an API prerequisite and
must not be put in a header.
## Prototype and include boundary
The corrected performer needs no prototype or type beyond the four owner
domains it names: go-to, wait, fire-target, and combat-status. Its callees
are already declared by their genuine headers.
The begin proposal needs no additional shared type/prototype beyond the
modifier domains, unit impulse, and three reference records above. Two
direct-owner includes are required when it is placed into the real TU:
- `#include "units/unit_definitions.h"` for the
`unit_definition_get(index)` owner macro and complete
`struct unit_definition` dereference;
- `#include "cutscene/recorded_animations.h"` for
`boolean recorded_animation_play(long unit_index, short animation_index)`.
Preserve the existing direct `#include "math/real_math.h"`; it owns the
normalization/vector APIs, including the accepted 3D-prefix-as-2D convention.
`actor_move_keep_moving_past_destination(long)` is already correctly owned by
`actors.h` and matches the definition in `actor_moving.c`. Do not add any
caller-local extern declarations.
All other required owners are already present: `actions.h`, `actors.h`,
`actor_definitions.h`, `ai_communication.h`, `props.h`, `objects.h`,
`scenario.h`, `units.h`, `bipeds.h`, `hs.h`, and the recorded-animation,
object-definition, player, data, and C runtime owners selected in the natural
candidate skeleton.
## Evidence and provenance
Authoritative local artifacts:
- PDB:
`../../research/hcea_jun2011_prototype/payload/Halo CE Anniversary (Jun 24 2011)/HCEX_Release.pdb`,
SHA-256
`0fd11b084fd2169cafe071fd8c3e4083d6e9564f01e7964f6c26e5a03eee4dff`;
- secondary PDB:
`../../research/hcea_jun2011_prototype/payload/Halo CE Anniversary (Jun 24 2011)/HCEX.pdb`,
SHA-256
`f55cfe957da8079a62a26a6753f1ddbb700b067a66de0bd5db5aac182d985ff1`;
- DIA reader: `../../research/tools/DIA2Dump/x64/Release/DIA2Dump.exe`,
SHA-256
`5be3b890fb5c3b7c74b7c3ce7c275a70f48813c388d3611c41e69c0ac0626243`;
- January `action_obey.obj`: current split artifact SHA-256
`13a4e47de2c79a3122033539284d148cae052f5f008f084a3d15862dda844368`;
the earlier frozen source-body audit recorded SHA-256
`0e4f889a35b97c44f6ae91e4906b547bd64f2ff12b9f399e02b46f02b69633fb`
before later semantic target-metadata regeneration;
- retained full-domain header:
`../../research/players_update_cluster_research/upstream/demon/source/ai/ai_scenario_definitions.h`,
SHA-256
`d650fce71f3f8f1cc9e1c5e02b350bb56b65b8de385ef1a7d93257410de2106c`.
Immutable HCEA database extracts, used as corroborative reconstructions rather
than original source:
```text
33fb4dd6bdc543a755d068186d18f472e679bcfd12835ffa020866dbef33db27 ai_atom_animation_mode_modifier.h
b9cf105849bd637b2c1b7011578ddb204f67e35ac5dce696eb6217a12323e412 actor_atom_grenade_modifier.h
586f4f8473f15d34370598a74ae3637a4d377ca37b938398de892ca0633deed8 ai_atom_vehicle_modifier.h
66b289cef8e9af8e497f552bdbf2a149b0396d6814800f0db1d7d82f73aa0570 ai_atom_look_modifier.h
df5ed09edc01002f3568b353cdf5ae24784a294b8ddcc92d9b6ccf3c9171851c ai_atom_animate_modifier.h
1919399bdc4cf367a73e1e71d7c17abdf5a2c5b481b40b97ea6e020362c832fc ai_atom_action_modifier.h
6d367a2adacf78a8c89b7e8a2cc566572e5fd0419fe92fd979affcd5abe74020 unit_animation_impulse.h
c34d83224cf95b071a9de847bdd1a98e63bde836ae6d622beb3351a990b2da72 ai_animation_reference_definition.h
16f251ee304111c333f39f2a495da1b65dcd91d154a151592a8f2f81ac63330f ai_script_reference_definition.h
b96e456964ea532b65afac96fbf4360f8cee954d51beb0ab374127a0ced4f68d ai_recording_reference_definition.h
```
All ten files are under
`../../research-cache/halocea-full-blobs-20260830/src/headers/`.
Final supporting scratch revisions:
```text
630ee045a17f3a095929516cd524c1ee40a385eb08d75a2b133ed99ebcca5ead action_obey_command_begin_proposal_20260905.c
1a9392214a2ca5be3eb0f214d38cc0c67f3d18ddabaa339df27165c7726c774b action_obey_command_perform_proposal_20260905.c
46e78d3c070638fab00d3f0c5334088b91146f92224330a5964ee4b358d8cd57 action_obey_actor_enum_owner_packet_20260905.md
1e554e3d6f43775d645302ffb4d0eb297e7e65d51983a938b006282f836a23af action_obey_command_begin_proposal_issues_20260905.md
```
## Provenance limits and final boundary
- Enum tag names are not PDB-authenticated; use anonymous enums.
- PDB proves the value enumerators described above. Terminal counts and the
non-emitted counterparts in two-valued domains are domain-completion
evidence from the retained source/database and January range behavior.
- The three scenario-reference records are stronger: every field name/type,
offset, and size is direct PDB evidence. No field is a speculative facade.
- HCEA `src/blam` bodies and extracted headers are binary reconstructions, not
recovered first-party source.
- The frozen begin's numeric boolean comparisons are known owner-name debt,
not an invitation to edit or compile another variant now.
- The failed 86-consumer owner trial is the current integration blocker.
Reopen only with a genuinely changed shared-header/consumer context, not
spelling, enum-tag, declaration-order, include-order, or compiler-steering
experiments.
@@ -1,163 +0,0 @@
# ActionObey: shared owners and structure-cache flush reconciliation
Date: 2026-09-05. Parent publication: `31a1927c951479106d0475ee99e1fb236a15e89d`.
## Accepted checkpoint and accounting
This checkpoint adds the genuine `action_obey_flush_structure_indices` leaf
and repairs the shared types, enum owners, and public API declarations needed
by the larger Fable ActionObey packet. It does not import that packet wholesale.
- ActionObey advances from 9/27 to **10/27 exact functions**, 439 to **485
meaningful code bytes**, and 480 to 528 padded bytes.
- Incremental credit is **46 meaningful / 48 padded bytes**, one function,
and no data. The complete-object count does not change.
- The final canonical build reports **910,347 / 2,198,102 meaningful exact
code bytes**, **6,187 / 11,060 credited functions**, **2,061,020 / 4,176,062
matched data bytes**, and **391/833 Matching objects**.
- The complete stable sweep advances 6,232 to **6,233** exact owners out of
8,245 measured owners, with zero inherited exact losses. This owner census
is not the same denominator or admission policy as the credited-function total.
Canonical root:
`C:/Users/isabe/Documents/Codex/2026-07-13/i-w/work/rasterizer-model-wrapper-alias-20260829`.
Donor root, read-only throughout:
`C:/halo-worktrees/fable-50k-small-families-20260904`, tip
`f9dd42218b3325e1b3a63d7860e5309f69fd3d8e`.
## Source ownership and credibility
`actions.h` owns the public ActionObey declarations. The packet adds the nine
genuine target-public declarations in its existing ACTION_OBEY section and
removes their six consumer-local copies from `actions.c`. The redundant
`actor_action_change` declaration in `action_obey.c` is removed; its existing
owner declaration is unchanged.
The leaf uses `actor_get`, the complete `obey_state_data`, the named
`destination_valid` and `destination_surface_index` fields, `NONE`, and an
explicit return. January independently materializes the obey state at actor
offset 0x9C, tests the byte at 0xCC, and conditionally stores -1 at 0xDC.
Provenance adjudication: this is an authenticated exact reconstruction, **not
a natural first-shot result**. Fable's report says the local `state_data`
pointer corrected an earlier missed candidate. That history remains visible
in the independent review. The accepted pointer is ordinary meaningful source,
matches adjacent action-family source organization, and is independently
supported by the target state-base computation. It is not an inert lifetime
anchor, aliasing trick, or invented control flow. This ruling does not authorize
replaying exhausted spelling/order searches on other functions.
The shared owner correction is one coherent packet:
- `actors.h` owns the full 0x40 `swarm_component_datum`, the 0x14
`swarm_wander_control`, the five component flags, and `swarm_component_get`.
Direct HCEX and HCEX_Release PDB records prove signed `short flags`, the
explicit byte `pad` at 0x1B, and the complete anonymous wander/obey union at
0x1C. The wander record has ordinary implicit alignment, not an invented
named padding field. Local partial/duplicate views are removed from
`actors.c`, `actor_type_infection.c`, and `actor_perception.c`.
- `actions.h` owns the five simple-control bits. Existing 0x24/0x58/0x84
obey record shapes are unchanged. Infection's local copies are removed.
- `bipeds.h` owns the six directly authenticated runtime bits. Bit zero is
airborne, not limping. The five misleading uses in `units.c` and one in
`players.c` are renamed; duplicate local enums in Infection, Bipeds, and
PlayerControl are removed. All values and underlying datum storage remain
unchanged. Units remains **189/189 exact**.
- `ai_scenario_definitions.h` owns the five authenticated command-list bits,
loop-until modifier, and four movement-facing values. The latter names are
explicitly descriptive reconstructions from January behavior, not claimed
recovered spellings. Unneeded later manual-BSP and count names are not imported.
- `actors.h` owns `actor_move_keep_moving_past_destination(long)` and
`action_charge.c` loses its foreign local declaration.
- `recorded_animations.h` owns the genuine play/play-and-delete/play-and-hover
signatures `(long, short)`. `hs.c` includes that owner and loses the three
mismatched local `(long, word)` declarations. Definition and caller code
are unchanged.
No private begin/perform facade, flat obey-only swarm view, new raw offset,
new inline directive, or new math wrapper is introduced by this checkpoint.
It does not bless the inherited ActionObey math-wrapper/callback debt: that
requires the real private caller graph and remains the next reconciliation step.
## Verification and the two residual changes
The public-header phase freezes all 47 actual Ninja consumers: 3,079 runtime
sections unchanged, 54 compiler-local-label spelling changes, and no runtime,
owner, COMMON, or forbidden point-helper addition. All 6,232 exact owners survive.
The subsequent shared-schema phase freezes all **58 actual consumers**, not
merely lexical include matches. Its full runtime comparison has 4,544 unchanged
sections, 62 compiler-local-label-only changes, and exactly two residual code
changes. Neither was accepted from strict-function counts alone:
1. `infection_swarm_control` remains 3,360 padded bytes with 103 relocations.
Three clear-bit masks use shorter signed-immediate encodings after the
authenticated signed-word field correction. Exhaustive evaluation covers
every 16-bit flag value at all three sites: **196,608 comparisons pass**.
The first two sites explicitly zero upper EAX. The third does not require
that assumption: only AL is tested and AX stored; TEST replaces the flags,
and both successor paths fully define EAX before reading it full-width.
All 130 direct branches, both table jumps, all 103 relocation records,
shifted local labels, exact allowed alignment bytes, and the masked
80-byte table/tail are checked. No owner, data, COMMON, or definition changes.
2. `ai_communication_update_speech_timers` remains 672 bytes with the same 43
ordered relocations. Its only change swaps the independent loads of DI
from `[EBP+0x10]` and EBX from `[EBP-8]` in 0x167..0x16E. All 27 branches
are unchanged; none enters the block interior; there is no indirect jump
or relocated operand in the block. The rest of the bytes are identical.
Its honest fuzzy score returns from 97.76382% to the previously observed
**97.67839%**. This is explicitly disclosed, not hidden as an exact gain.
No speech-timer source or scheduling hint was changed.
The first Ninja invocation compiled the consumers but correctly stopped its
progress step on the now-stale speech-timer evidence. Only after full semantic
review and independent proof was that park remeasured. Final Ninja succeeds.
Root independently replays the hardened residual proof with the same JSON hash.
The final 58-object freeze matches the reviewed after-freeze across all **4,608
runtime sections** with no changes at all.
Final checks:
- Complete stable sweep: +1 exact function / 48 padded bytes, zero losses.
- Leaf: **1,024 cases each** against the January target, final actual object,
and isolated build. All 256 flag-byte representations and four surface
values; full modeled actor-state writes, call arguments, stack, preserved
registers, and direction flag checked. Limit: valid modeled actor handle
and `datum_get` contract, not a whole-game execution test.
- Separate VC7 scratch TU: **24 compile-time layout/value checks** pass;
it emits no code and earns no progress credit.
- **288 tests plus 26 subtests pass** after the final park update.
- Parks: **284 active, zero stale, zero invalid**.
- Admission: zero candidates, zero contradictions, five existing displayed
rejections, zero revocations. No new `_point_from_line3d` emission/reference.
- Protected bitmap source/header hashes are unchanged. No source implementation
in the new Opus rasterizer/device/widget reservation is modified.
## Frozen evidence and continuation
Key manifests (all under canonical `scratch/`):
- `action-obey-public-api-before-20260905.json` / `...after...`:
`22082d37d81366a681520949d9502107f7ea43b8e185f29936643cd07c89d0d5`
/ `49d6f456321f119a67d25ac7324d82b4fc282d43cf1e86044f4a342e43284f65`.
- `action-obey-shared-schema-before-20260905.json` / `...after...` / `...final...`:
`90becfaff10da7f9e549e43d28385da41c37675d51d3c9319347bb7b71600253`
/ `b7fef54d4e8030efd58299c5dd290f7e3e9a0ba0c80e8bd8aaa70a929d95c831`
/ `d88762b5f013727b37c746f8e290a5719779e40ec00a9ae10b5accc6e9fd24bb`.
- `action_obey_shared_schema_residual_independent_proof_v2_20260905.json`
and root replay both hash
`2a673deafe4714f0ad74ff074feb24d8cb34cb4f87fe6485fbca89d5911c2e4f`.
- `action-obey-structure-leaf-final-runtime-20260905.json`:
`b4ce9623dafa0c31d541f6a6aa16528c3c843522f542c47e367c45566b08e059`.
See the sibling API/leaf independent review, shared-schema evidence, and
shared-schema residual proof ledgers for source hashes and detailed ownership.
The private-closure plan is preserved separately. Both real private command
bodies, further actor/modifier enum ownership, and ordinary math-header
materialization remain scratch research until their coherent packet passes.
Do not count the donor's remaining exact claims or these proposals as integrated.
The separate Bitmaps intake preserves 18 distinct donor exact candidates /
4,736 padded bytes, with prototype/ownership/provenance issues still awaiting
reconciliation. It adds no canonical credit in this checkpoint. The Fable lane
as a whole remains unfinished; donor committed and dirty work is preserved.
@@ -1,340 +0,0 @@
# ActionObey shared swarm schema and enum-owner audit (2026-09-05)
## Disposition
The shared `swarm_component_datum` / `swarm_wander_control` schema is
authenticated strongly enough for one coherent owner-header trial. The
natural owner is `source/ai/actors.h`: that header already owns
`swarm_datum`, `SWARM_COMPONENT_DATUM_SIZE`, `swarm_data`,
`swarm_component_data`, and the neighboring actor/swarm accessors.
The decisive evidence is direct DIA type information in both `HCEX.pdb` and
`HCEX_Release.pdb`, not the Fable donor's flat local view. In particular,
`swarm_component_datum.flags` is a signed `short`, the byte at `+0x1B` is an
explicit member named `pad`, and the `+0x1C` tail is an anonymous union of
`wander` and `obey`. `swarm_wander_control` has no named member at `+2`;
that two-byte interval is ordinary alignment before its vector.
This is a read-only/source-ownership result. No production, donor, header,
configuration, build, or object file was changed or compiled by this audit.
The current root-owned ActionObey API/flush-leaf work was observed but not
modified or re-audited here.
## Direct PDB schema
Reproduction tool:
`C:/Users/isabe/Documents/Codex/2026-07-13/i-w/research/tools/DIA2Dump/x64/Release/DIA2Dump.exe`
Primary type source:
`C:/Users/isabe/Documents/Codex/2026-07-13/i-w/research/hcea_jun2011_prototype/payload/Halo CE Anniversary (Jun 24 2011)/HCEX.pdb`
SHA-256: `f55cfe957da8079a62a26a6753f1ddbb700b067a66de0bd5db5aac182d985ff1`
Independent same-result PDB:
`C:/Users/isabe/Documents/Codex/2026-07-13/i-w/research/hcea_jun2011_prototype/payload/Halo CE Anniversary (Jun 24 2011)/HCEX_Release.pdb`
SHA-256: `0fd11b084fd2169cafe071fd8c3e4083d6e9564f01e7964f6c26e5a03eee4dff`
The relevant commands are `DIA2Dump.exe -sym <name> <pdb>`. Both PDBs give
the same two principal layouts:
| type/member | offset | DIA type |
| --- | ---: | --- |
| `swarm_component_datum` | | size `0x40` |
| `identifier` | `0x00` | `short` |
| `flags` | `0x02` | `short` |
| `position` | `0x04` | `real_point3d` |
| `surface_index` | `0x10` | `long` |
| `combat_target_prop_index` | `0x14` | `long` |
| `attached_to_unit_ticks` | `0x18` | `unsigned char` |
| `ground_ticks` | `0x19` | `unsigned char` |
| `attack_delay_ticks` | `0x1A` | `unsigned char` |
| `pad` | `0x1B` | `unsigned char` |
| `wander` | `0x1C` | `struct swarm_wander_control` |
| `obey` | `0x1C` | `struct obey_individual_simple_control` |
| `swarm_wander_control` | | size `0x14` |
| `pause_ticks` | `0x00` | `unsigned char` |
| `move_ticks` | `0x01` | `unsigned char` |
| implicit alignment | `0x02..0x03` | no DIA member |
| `vector` | `0x04` | `real_vector3d` |
| `angle` | `0x10` | `float` |
The already-shared ActionObey arm in `actions.h` also agrees with the PDB:
`obey_individual_simple_control` is `0x24`, with bytes at `+0/+1`, signed
`short pause_timer` at `+2`, bytes at `+4/+5`, and anonymous
`directmovement` / `jump` arms at `+8`. The direct-movement arm is `0x1C`
(`short facing +0`, vector `+4`, start position `+0x10`); the jump arm is
`0x0C` (`short delay_ticks +0`, reals `+4/+8`). Those existing records need
no shape change.
`DIA2Dump -sym swarm_component_data HCEX.pdb` identifies the global as
`struct data_array *`, matching the existing `actors.h` declaration.
## Source-authentic owner shape
The following is the evidence-backed shape for `actors.h`. The enumerator
count is deliberately omitted because the named `NUMBER_OF_*` symbol is not
present in either PDB and no current consumer needs it.
```c
enum
{
_swarm_component_attacking_in_melee_bit = 0,
_swarm_component_attached_to_unit_bit,
_swarm_component_wander_bit,
_swarm_component_obey_bit,
_swarm_component_obey_desire_jump_bit,
};
struct swarm_wander_control
{
byte pause_ticks;
byte move_ticks;
real_vector3d vector;
real angle;
};
struct swarm_component_datum
{
short identifier;
short flags;
real_point3d position;
long surface_index;
long combat_target_prop_index;
byte attached_to_unit_ticks;
byte ground_ticks;
byte attack_delay_ticks;
byte pad;
union
{
struct swarm_wander_control wander;
struct obey_individual_simple_control obey;
};
};
```
Owner assertions should lock, at minimum:
- `sizeof(swarm_wander_control)==0x14`, vector `+4`, angle `+0x10`;
- `sizeof(swarm_component_datum)==0x40`, flags `+2`, position `+4`,
surface `+0x10`, target prop `+0x14`, the four bytes `+0x18..+0x1B`, and
both union arms `+0x1C`; and
- the already-proven composed access
`obey.directmovement.vector==+0x28`.
The retained HCEA generated header spells an explicit `pad[2]` in
`swarm_wander_control`. That is a reconstruction convenience, not a named
member recovered by DIA, and must not be promoted into the owning schema.
## Signed flags: independent January corroboration
January `_code_00005350` (`action_obey_individuals_iterate`, `0x130` padded)
zeros exactly `0x24` bytes at component `+0x1C`, then loads the word at `+2`
and emits:
```text
xor eax,eax
mov ax,word ptr [esi+2]
and eax,0xfffffffb
or eax,8
mov word ptr [esi+2],ax
```
This is the component's `wander`-clear / `obey`-set operation. The frozen
Fable measurement records that `short flags` reproduces the
`and eax,0xfffffffb` encoding while its prior unsigned `word` spelling emits
the different `and eax,0xfffb` form. That code observation corroborates the
direct PDB `short`; it is not being used as a spelling-search instruction.
The committed Fable source must therefore not contribute its flat
obey-only component record, even though that local record also uses `short`.
Its source SHA-256 is
`067b4900d5494c5341ca609337f0d220dedd4f95778a935d601427de96575a59`.
## Enum owners and provenance tiers
### `actors.h`: component flags
Direct `HCEX.pdb` constants authenticate all five spellings and values:
| enumerator | value |
| --- | ---: |
| `_swarm_component_attacking_in_melee_bit` | 0 |
| `_swarm_component_attached_to_unit_bit` | 1 |
| `_swarm_component_wander_bit` | 2 |
| `_swarm_component_obey_bit` | 3 |
| `_swarm_component_obey_desire_jump_bit` | 4 |
These belong with `swarm_component_datum.flags` in `actors.h`.
`NUMBER_OF_SWARM_COMPONENT_FLAGS` is absent from the PDB and unused by the
candidate; do not add it merely because a later extracted DB header has it.
### `actions.h`: simple-control flags
Direct `HCEX.pdb` constants authenticate all five spellings and values:
| enumerator | value |
| --- | ---: |
| `_obey_simple_directmovement_bit` | 0 |
| `_obey_simple_directmovement_update_continuously_bit` | 1 |
| `_obey_simple_jump_bit` | 2 |
| `_obey_simple_jump_jumped_bit` | 3 |
| `_obey_simple_jump_targeted_bit` | 4 |
They belong beside `obey_individual_simple_control.simple_control_flags` in
`actions.h`. `NUMBER_OF_OBEY_SIMPLE_FLAGS` is absent from the PDB and unused,
so the proposed owner packet need not invent it.
### `bipeds.h`: runtime biped flags
The PDB defines `_biped_datum.flags` as `unsigned long` and directly
authenticates the complete six-name set:
| enumerator | value |
| --- | ---: |
| `_biped_airborne_bit` | 0 |
| `_biped_slipping_bit` | 1 |
| `_biped_absolute_movement_bit` | 2 |
| `_biped_no_collision_bit` | 3 |
| `_biped_movement_passes_through_bipeds_bit` | 4 |
| `_biped_limp_body_physics_active_bit` | 5 |
These belong next to `_biped_datum.flags` in `bipeds.h`.
`NUMBER_OF_BIPED_FLAGS` is not returned by DIA and is unnecessary.
The existing header's `_biped_limping_bit=0` is not an authentic alias:
`DIA2Dump -sym _biped_limping_bit` returns no symbol, while bit 0 is directly
named `_biped_airborne_bit`. It is also not unused. Current source has five
uses in `units.c` (lines 2364, 4219, 4302, 6547, and 10051 at audit time) and
one in `players.c` (line 2111). The strongest semantic anchors select
`_unit_state_melee_airborne` and `_unit_state_dying_airborne` when the bit is
set. A coherent owner repair must rename those six uses to
`_biped_airborne_bit`, remove the wrong placeholder, and not retain it as a
second same-value name.
### `ai_scenario_definitions.h`: command-list flags and atom modifiers
`ai_command_list_definition.flags` is directly `unsigned long` at `+0x20`.
The PDB authenticates exactly these five command-list names:
| enumerator | value |
| --- | ---: |
| `_ai_command_list_allow_initiative_bit` | 0 |
| `_ai_command_list_allow_targeting_bit` | 1 |
| `_ai_command_list_disable_looking_bit` | 2 |
| `_ai_command_list_disable_communication_bit` | 3 |
| `_ai_command_list_disable_falling_damage_bit` | 4 |
January `action_obey_command_list_setup` independently reads masks
`1/2/4/8`, and `action_obey_individual_begin` reads mask `0x10`.
`_ai_command_list_manual_structure_bsp_bit=5` and
`NUMBER_OF_AI_COMMAND_LIST_FLAGS=6` occur in later extracted DB/Demon
material but are not returned by either PDB and are unused by the evidenced
eleven-function donor subset. They must not be included in this PDB-grounded
packet.
`ai_command_definition.atom_modifier` is directly a signed `short` at `+2`.
The PDB authenticates only
`_ai_atom_loop_modifier_until_told_to_advance=1`; January command-end compares
the word against 1. The donor's `_ai_atom_loop_modifier_always=0` and count
are not needed and have no direct PDB result, so omit them.
The direct-movement facing domain is the one remaining naming caveat.
January `_code_00005f70` proves a signed-short read at simple-control `+8`, an
unsigned range check for values `0..3`, and these behaviors:
- 0: copy the base facing;
- 1: negate the base facing;
- 2: store the normalized perpendicular;
- 3: negate that perpendicular;
- any other value: leave the output unchanged.
This authenticates the values and semantics of the donor spellings
`_ai_atom_move_facing_forwards/backwards/left/right`, and the natural owner is
the atom-modifier domain in `ai_scenario_definitions.h`. It does **not**
authenticate those exact source spellings: DIA returns no such names and the
retained HCEA function explicitly records that its enum oracle found none.
If descriptive reconstructed enum names are allowed with an explicit
provenance comment, those four donor names are semantically credible. If an
original-name requirement applies, the private direct-movement/update pair
remains withheld; raw integers or a TU-private enum are not acceptable
workarounds.
## Duplicate/facade removal inventory
One coherent shared-owner trial must address all current production views,
not only the donor file:
| file | current debt | owner-coherent action |
| --- | --- | --- |
| `source/ai/actors.c` | partial `swarm_component_datum` with unsigned `word flags` and `unknown_tail`; local accessor macro; two local layout typedefs | remove the partial type; consume the owner type; relocate/remove redundant asserts; use the shared typed accessor if installed |
| `source/ai/actor_type_infection.c` | local component/wander types (`word flags`, synthetic named `word pad`); local component and obey-simple enums; local biped-airborne enum; local accessor and layout typedefs | remove duplicate types/enums/asserts and consume `actors.h`, `actions.h`, and `bipeds.h` owners |
| `source/ai/actor_perception.c` | 0x40 prefix facade `actor_perception_swarm_component_view`; local accessor; raw `TEST_FLAG(...,1)` | remove facade/assert, use `swarm_component_datum`, and name bit 1 `_swarm_component_attached_to_unit_bit` |
| `source/units/bipeds.c` | local bit 0 / bit 5 enum | remove it after the complete owner enum lands |
| `source/game/player_control.c` | local `_biped_airborne_bit=0` enum | remove it after the owner enum lands |
| `source/units/units.c`, `source/game/players.c` | six uses of wrong `_biped_limping_bit` name | migrate all six to direct-PDB `_biped_airborne_bit` |
| prospective Fable `source/ai/action_obey.c` body | flat obey-only component, local component/simple/biped/command/modifier enums, local accessor | do not import those definitions; consume the owning headers and shared full-union type |
`actor_type_infection.c` also copies the field into a local
`word component_flags`. DIA proves the **record field** is signed; it does
not recover that local variable's source type. Do not retune this local as
part of the ownership packet without January function evidence—the header
correction alone removes the false record declaration.
Because `actors.h` already owns `swarm_get`, `swarm_try_and_get`, and the
`swarm_component_data` declaration, a single typed `swarm_component_get`
macro there is the natural source-owner cleanup for the three current local
macros and the future ActionObey caller. Macro ownership is a source-design
inference (PDBs do not retain macros), not a recovered original symbol; root
may instead retain identical local macros, but the perception facade's return
type still must be corrected.
## Trial boundary and dependency scope
This audit found 43 direct lexical includers of `actors.h` (42 C files plus
`ai_debug.h`), 27 direct includers of `actions.h`, 13 of
`ai_scenario_definitions.h`, and 13 of `bipeds.h`. These are only source-text
counts, not a substitute for the build dependency graph; the root-owned
trial must freeze and compare every actual dependent object and all inherited
strict/residual owners.
The first coherent trial should therefore be one ownership packet:
1. install the PDB-authenticated full schema and five component bits in
`actors.h`;
2. remove the three production type facades and migrate perception's raw bit;
3. install the five PDB-authenticated obey-simple bits in `actions.h`;
4. install the six PDB-authenticated biped bits in `bipeds.h`, remove three
local enum bodies, and repair all six wrong bit-0 consumer names;
5. install only the PDB-authenticated command-list bits 0..4 and loop-until
value 1 in `ai_scenario_definitions.h`;
6. decide explicitly whether the four behavior-authenticated but
source-name-unproven move-facing enumerators are acceptable; and
7. only then compile the unchanged natural ActionObey donor-body subset under
real owner headers, with a complete dependent-object/runtime census.
No enum counts, manual-BSP bit, flat union, synthetic wander pad, raw bit
numbers, prefix views, or TU-private fallback enums are part of this
recommendation.
## Evidence read
- `scratch/fable_action_obey_intake_20260905.md`, SHA-256
`587bda8055043f26957944b40939f0a7e9a831957c8cace07e0d3c007238eef6`;
- current `actors.h/.c`, `actor_type_infection.c`, `actor_perception.c`,
`actions.h`, `ai_scenario_definitions.h`, `bipeds.h/.c`,
`player_control.c`, `units.c`, and `players.c`;
- the committed Fable `action_obey.c` at
`f9dd42218b3325e1b3a63d7860e5309f69fd3d8e`, its saved
`scratch/reports/action_obey.md`, and its frozen exactness notes;
- complete January disassemblies for `_code_00005350`,
`_action_obey_command_list_setup`, `_code_00005290`, `_code_00004fb0`, and
`_code_00005f70` from `build/split/source/ai/action_obey.obj`;
- direct DIA queries against both PDBs for all types and enumerators recorded
above; and
- retained HCEA generated headers/functions and Demon command-schema source,
used only as corroboration where explicitly identified, never as January
original-source proof.
@@ -1,83 +0,0 @@
# ActionObey shared-schema residual proof (final)
Date: 2026-09-05
Scope: the two `runtime_or_owner_changed_REVIEW` sections selected by the frozen
58-consumer shared-schema audit. This proof reads only the frozen before/after
inventories and objects. It does not read live objects or source, compile,
run a gate, refresh a park, or decide packet ownership.
## Frozen inputs
- Before inventory SHA-256:
`90becfaff10da7f9e549e43d28385da41c37675d51d3c9319347bb7b71600253`
- After inventory SHA-256:
`b7fef54d4e8030efd58299c5dd290f7e3e9a0ba0c80e8bd8aaa70a929d95c831`
- Infection objects:
`8bb55ad955ced508c5db6cd768ef437575b3b6e6a83f2140757282854197f531`
-> `80caf88f6bc3db25ecffa0488ac480eba0997618be066f5d2cfca6b8fd76de62`
- Communication objects:
`329718faba04602a5809c17849c23f824ee483b4f854c58bc62d11c7c3e97141`
-> `be4f557e04d2b99155594435035bef9705c6e317e85162efe9494d1f5909ed5e`
## Result
Both residuals preserve their public ABI, COFF owner/linkage/type/value,
section flags/alignment/COMDAT selection, relocation identities and topology,
control-flow topology, and observable behavior for a valid call.
### `_infection_swarm_control`
- Size remains 3,360 bytes and all 103 relocations map exactly. Thirty-eight
relocation source offsets move with the shortened encodings. The six
changed internal table destinations map as
`1254->1252` three times, `1701->1697` twice, and `1793->1789` once; the
table slots themselves remain fixed.
- All 130 direct branches and both indirect table jumps map exactly. No branch
enters either alignment/trailing-padding interior.
- The only executable changes are three `AND EAX, imm32` encodings becoming
their sign-extended `imm8` forms: `0x0000fffe -> 0xfffffffe` twice and
`0x0000fff7 -> 0xfffffff7` once. Exhaustive evaluation of all 65,536 16-bit
flag inputs at all three sites (196,608 comparisons) is exact.
- The first two sites explicitly zero EAX before loading AX. At the third
site, upper EAX is not assumed zero: only AL is tested and AX is stored;
`TEST AL,1` overwrites the `AND` flags before the branch, and each successor
fully defines EAX before any full-width read.
- The exact allowed padding bytes are asserted, not merely described: old
`LEA ESP,[ESP]` becomes `JMP 0x940` plus skipped `LEA EBX,[EBX]`; the old
post-`RET` `NOP` becomes post-`RET` `LEA ECX,[ECX]`.
- After masking relocation slots, the complete 80-byte `[0xcd0,0xd20)` table
and tail are byte-identical, SHA-256
`eb8ad26cf1b215d3d0731e00af0d53823547fa6cd4254298bafa1996d58f96c1`.
- No runtime/data section was added or lost, no COMMON changed, no definition
transition occurred, and the complete function-owner name inventory is
unchanged. Shifted compiler-local labels are fully accounted for by the
same offset map.
### `_ai_communication_update_speech_timers`
- Size remains 672 bytes and all 43 relocation records are byte-for-byte
identical.
- The only change is the order of adjacent loads in `[0x167,0x16e)`:
`DI <- word [EBP+0x10]` and `EBX <- dword [EBP-8]`. They read distinct
stack slots, write disjoint registers, and do not touch flags or memory.
- Bytes before `0x167` and from `0x16e` onward are identical. All 27 direct
branches are identical, two enter at the block boundary `0x167`, the jump at
`0x165` rejoins at `0x16e`, no branch targets the strict interior, there are
zero indirect jumps, and the block contains zero relocations.
- No runtime/data section was added or lost, no COMMON changed, no definition
transition occurred, and the complete function-owner name inventory is
unchanged.
This is a frozen-object equivalence proof for these two deltas, not a blanket
exception for future schema packets or other compiler-shape changes. It does
not replace the root owner's admission, park, build, or stable-gate decision.
## Artifacts
- Hardened proof script SHA-256:
`b9aa2a4c8b4509c368b9bbd96d5907abd4944202b635edc29c852807e2df74b0`
- Final v2 JSON SHA-256:
`2a673deafe4714f0ad74ff074feb24d8cb34cb4f87fe6485fbca89d5911c2e4f`
- Preserved preliminary v1 JSON SHA-256:
`b44e5ed4c6d5e469a0ae821fbd4b91aa6af04393242238d0df7e4670081226c8`
@@ -1,121 +0,0 @@
# `action_search.obj` exact-match strategy ledger
## Outcome
`source/ai/action_search.obj` is strict-exact: all 9 functions and all 4
owned `.rdata` COMDAT sections match the January 2002 target in padded size,
normalized bytes, relocation count, relocation address/type, and resolved
destination/addend.
The reconstruction began from the HCEA-to-January bridge packet, but every
prototype, field offset, constant, call, and source shape was independently
gated against the January object. Cross-build evidence was used only as a map;
January bytes remained the authority.
## Final function inventory
| Function | Padded bytes | Relocations | Result |
| --- | ---: | ---: | --- |
| `action_search_setup_target` | 128 | 7 | exact |
| `action_search_setup_pursuit` | 256 | 10 | exact |
| `action_search_setup_undirected` | 112 | 7 | exact |
| `action_search_update` | 384 | 9 | exact |
| `action_search_flush_position_indices` | 48 | 2 | exact |
| `action_search_flush_structure_indices` | 48 | 2 | exact |
| `action_search_control` | 336 | 3 | exact |
| `action_search_begin` | 144 | 7 | exact |
| `action_search_perform` | 848 | 24 | exact |
## Closeout discoveries
### `action_search_setup_pursuit`
The initial reconstruction used an outer eligibility condition plus a nested
firing-position guard. That produced the right behavior and size but the wrong
cold-epilogue ordering. The exact source is one chained guard containing
`firing_position_index != NONE`. VC7 then reuses `BX` for the firing-position
index and automatically emits the three January return paths in the correct
order. This also satisfies the project's preference for one source-level
return.
General lesson: when several failed predicates share one machine epilogue,
write them as one short-circuit condition before trying manual early returns or
gotos. The compiler's SSA split may create multiple machine returns even when
the C has one return.
### `action_search_perform`
Three source details were required:
1. Keep the maximum-searcher comparison explicitly `short`. Without the cast,
integer promotion creates a `movsx` and a 32-bit compare instead of the
January 16-bit compare.
2. Write the movement dispatch as a flat chain beginning with
`if (at_destination)`, followed by the type-0 and type-1 branches. The
semantically equivalent nested `if (!at_destination) ... else ...` makes
VC7 share the two move-call suffixes in the opposite direction.
3. Declare one function-scope `struct prop_datum *prop` and reuse it for both
prop lookups. Separate expression temporaries create an EAX/ECX mirror in
the examined-pursuit block; the reused named local reproduces January's
register coloring exactly.
The measured rejects included nested versus combined searcher conditions,
integer-promoted thresholds, early returns, a `switch`, explicit shared-result
gotos, declaration-order changes, `register`, and one- and two-barrier tail
experiments. All were reverted when they failed strict comparison.
## House-rule audit
- Parameters are one per line.
- Typed actor, prop, scenario, and tag-block accessors are used.
- Void functions end with explicit `return;` where their exact source permits.
- No inline assembly, volatile byte forcing, undefined behavior, compiler flag
change, fuzzy credit, or comparator weakening is present.
- `action_search_update` retains its early machine-required exit; forcing a
single source return would change exact codegen, so this is the documented
exception to the preferred single-return rule.
## Current-campaign revalidation (2026-08-13)
This atomic lane began at authoritative campaign tip `ea55318e`, where this
translation unit was still a 38-line skeleton and reported 0/9 exact functions.
Before reconstructing it again, the lane audited all local worktrees and
branches and found Claude/Fable's completed but unintegrated object at commit
`bd438b42`. Reusing that proven local result was both faster and safer than
repeating the source-shape search above.
The commit was cherry-picked into a new isolated worktree, configured against
the installed XDK 3911 compiler and pristine January executable, and rebuilt
from source. The hardened comparator independently reproduced every function
hash and relocation identity:
| Function | Padded bytes | Relocations | Normalized SHA-256 |
| --- | ---: | ---: | --- |
| `action_search_setup_target` | 128/128 | 7/7 | `cbf6d948839e88e8e724d10a38ac71c300c6e37440a51b2882923df9dc0a3cff` |
| `action_search_setup_pursuit` | 256/256 | 10/10 | `4347d9f3a5b25001bcdbf6f61d729394de70ebb908f1688e459654c2f10778e3` |
| `action_search_setup_undirected` | 112/112 | 7/7 | `dd5c14cc93d1ad3a623a90cc663dd57f974c84bdf8416e95d97219b585f1d933` |
| `action_search_update` | 384/384 | 9/9 | `86d23b9bcdbf51d55040b1aeb6342c751a18fdf31f4350606d8b07386b444d64` |
| `action_search_flush_position_indices` | 48/48 | 2/2 | `0092a713331f5179dbbff0cc7a9c576e7ebbe07d887f6ba6661da5713a0acd53` |
| `action_search_flush_structure_indices` | 48/48 | 2/2 | `828e1700de76a884cac45684d4e9eb16164aac1d8554ab01f9491b715d870b62` |
| `action_search_control` | 336/336 | 3/3 | `0152f72402462585a66e38af4f580d73fb27cfebe5d70f432efbd0d105926951` |
| `action_search_begin` | 144/144 | 7/7 | `f60a7b46a9e6640afb10e4cc818304c1a6d499f7d9eed99aa42e25b4e29488b9` |
| `action_search_perform` | 848/848 | 24/24 | `91321b997bee74aa54c2fea51c3c3ba35c30bedf5e4856f88b259984597d267d` |
The object owns no `.data` or `.bss`. Its four target-owned `.rdata` COMDATs
are the source path and the float constants `0.64f`, `6.25f`, and `0.49f`; all
four compare strict exact. Their normalized SHA-256 values are respectively
`cfd78efaa8180437a616198604af560523d4c0c77a2106385c170a77946efffd`,
`74fdba32aaac063fe5801c661ff89edff6e7852c241a36064ce88505748a5894`,
`c1080e69acd873d362d47f9e25351f5237bcf82e3374c88040dc7140aa378747`,
and `eeacb7c70a55988e1772bae66236c28b361bba5de119afb4075e752269a6890a`.
The `state_data` assertion string is target-external and candidate-owned due to
ordinary COMDAT folding; its relocation spelling and bytes are nevertheless
strictly resolved at every use by the hardened comparator.
The source was also manually re-audited against Berth's rules. Parameters are
one per line; `void` functions end with explicit `return;`; actor, prop, tag,
and tag-block access go through the typed subsystem interfaces; and the one
early return in `action_search_update` is retained only because January's
machine control flow requires it. No raw object/tag casts, alignment controls,
assembly, `volatile`, forced inline, undefined behavior, magic byte forcing,
or flag/comparator exception is present.
@@ -1,130 +0,0 @@
# `action_uncover.obj` credible fuzzy reconstruction and park
## Result
`source/ai/action_uncover.obj` now has source for every January function:
- 8 strict-exact functions;
- 1 measured credible residual;
- 0 unwritten functions.
The retained `_action_uncover_perform` is coherent typed C. It has the exact
544-byte padded envelope, 164-instruction count, and all 19 January relocation
addresses, types, destinations, and addends. Its objdiff score is 98.77778%.
Only 43 normalized byte positions differ, all within the contiguous
`+0xA2..+0xD3` pursuit-point copy schedule. The eight established exact
siblings remain exact, so the object retains 2,112 strict-exact padded code
bytes out of 2,656 and has no unwritten byte scope.
## Evidence and source reconstruction
The January split object and its disassembly remain authoritative. The frozen
ordinary-C candidate from the earlier `jonas/action-uncover-completion-20260828`
wave supplied the strongest same-compiler source topology. The following
cross-build material was read as corroboration, not as byte authority:
- HCEA commit `570c83fd9c365dad6f2a3e7041705d5b84c7847c`, whose manually recovered
`action_uncover_perform.c` agrees on the selector/change short ABI and the
high-level state transitions;
- Stian and pastudan retail-Xbox raw-pointer lifts, which corroborate the
branch flow and record offsets but are not admissible source;
- `punpckhdq/halo`, which still contains only the function stub;
- the open Marathon source tree, which has no direct firing-position/uncover
homolog and therefore supplies style precedent only.
The restored body:
1. asserts that the actor is not a swarm actor;
2. gates work on the actor timeslice, passenger state, and completion state;
3. constructs the firing-position search from either the pursuit location or
the last visible target position;
4. selects a candidate and updates the target-visibility or inspected-location
state using named line-of-sight and pursuit enums;
5. changes the actor's firing position and records failure when no position can
be adopted.
The source uses `boolean`, `real`, `real_point3d`, named enum constants, typed
records, and the existing C-series memory helper. It contains no inline
assembly, `volatile`, raw byte emission, optimizer directive, artificial
dependency, address-placeholder name, or helper call introduced only to steer
code generation.
## Residual boundary
| Measurement | January | Retained candidate |
| --- | ---: | ---: |
| Meaningful / padded bytes | 533 / 544 | 533 / 544 |
| Instructions | 164 | 164 |
| Relocations | 19 | 19 |
| Normalized SHA-256 | `ddd3ed044dc0de47ecb6ba83ac683372ff87b3cb7d8fee318d20da5467c39ed8` | `f6c5eb3588f3efccd66611ba0c133b20dc3fb449fad4894f3022b8e9c6f04bd0` |
| Objdiff fuzzy score | 100% | 98.77778% |
January loads all three `real_point3d` dwords before interleaving the
independent enable, surface, cluster, and point stores. VC7 lowers the natural
aggregate assignment with the same compact source `lea` but alternates the
point loads and stores. Code before and after that window realigns exactly.
The prior aggregate, scalar, alias, helper, field-order, scope, barrier, and
45 documented defined-C topology families are exhausted. Forcing the remaining
schedule would require gameplay assembly or artificial source, so the function
is parked as `instruction-scheduling` in `config/parked.json`. Reopen only for
authenticated January source/local records, a strict same-compiler C donor, or
a new natural lifetime fact that preserves the frame, relocations, compact
source `lea`, and exact downstream suffix.
## Prototype ownership and shared-header audit
`actor_active_select_firing_position` and `actor_change_firing_position` are
owned by `actor_firing_position.c`. Their forward-declared record tags and
short-width prototypes now live in the closest associated owner header,
`actors.h`, rather than in action translation units. The local full work-record
definitions remain in their consuming TUs because the owner object is still
largely unwritten and moving the large incomplete layouts into a shared header
would unnecessarily expose provisional definitions.
January callee disassembly independently establishes the narrow ABI: the
selector returns in `AX`, and the changer consumes the position index through
`DI` and returns in `AX`. Existing exact callers were reconciled to `short`
locals before removing their duplicate declarations. Rapid same-command-line
gates then compared the edited header against the pre-edit header for all 41
direct C includers and all five additional C consumers reached through
`ai_debug.h`. Every per-function exact/residual/unwritten set was unchanged.
In particular:
- `action_avoid.obj`: 6/6 exact;
- `action_fight.obj`: 6/6 exact;
- `action_guard.obj`: 15/15 exact;
- `units.obj`: 189/189 exact;
- `objects.obj`: 121/121 exact.
The same cleanup removed the final address-style private name from the touched
`action_guard.c`. The pinned HCEA PDB authenticates
`action_guard_speak_post_combat(long)` in `action_guard.obj`, and the recovered
HCEA body corroborates the January function's one-shot post-combat speech
semantics. January's PDB omits this private static, so the name is recorded as
later-build PDB provenance rather than claimed as January-name-proven. The
renamed January function remains strict exact.
## Ownership and validation
Relative to the declaration-only baseline, the only new owned production
sections are `_action_uncover_perform` itself and its three expected strings
(the swarm assertion, target-visibility diagnostic, and pursuit-inspection
diagnostic). No helper COMDAT was added. In particular, the candidate symbol
table contains no `point_from_line3d` symbol. Direct target comparison reports
all 304 target-owned data bytes exact.
Canonical integration validation completed after regenerating the split and
running a full Ninja build:
- canonical campaign gate: `8 exact / 1 residual / 0 unwritten`;
- exact-set comparison: no lost exact sibling;
- direct distance audit: 544 bytes, 43 differing normalized positions, 19
relocations, identical relocation address and destination sequences;
- objdiff: 98.77778% for `_action_uncover_perform`, 100% for all eight siblings,
and 100% for target-owned data;
- parked-function validator: 1 active, 0 stale, 0 invalid;
- fake-match scan across all edited AI source/header files: 0 findings;
- full tool suite: 254 passed;
- semantic report: 0 unit errors;
- common-key exact regression sweep: 0 losses;
- `git diff --check`: clean.
@@ -1,136 +0,0 @@
# `action_uncover.obj` perform ABI reconciliation and scheduler closeout
## Result
`source/ai/action_uncover.obj` remains honestly incomplete at 8/9 strict
functions and 2,112/2,656 padded code bytes. `_action_uncover_perform` is
still absent from the production candidate object; no residual body, owned
string, completion label, park, semantic exception, compiler option, or
comparator exception is admitted.
This wave resolves the contradictory selector-width history and exhausts a
new, bounded set of ordinary-C copy topologies. The best lawful candidate
has the exact 544-byte envelope, all 19 relocations, the same 164-instruction
count, and the complete January call/ABI schedule. Its only remaining
difference is
the already isolated pursuit-point copy scheduler: 43 normalized byte
positions in one contiguous `+0xA2..+0xD3` window.
## Evidence read first
The current recovery record
`docs/object_matching_logs/action_uncover_obj_jonas_recovery.md`, the fuller
historical strategy log at commit
`1633ccff6d9baa3f01b8e4eb3648b5880932e8be`, and the preserved earlier
cross-build closeout log were read in full. The historical log is decisive:
January callee disassembly proves all three related types are narrow:
- `actor_active_select_firing_position` returns `short`;
- the receiving `selected_firing_position_index` local is `short`;
- `actor_change_firing_position` receives that argument as `short` and returns
`short`.
The 2026-08-26 follow-up in the current recovery record narrowed the selector
and local while retaining a `long` consumer parameter. Its resulting
sign-extension was therefore an incomplete ABI experiment, not evidence of
January-width drift. Repeating the complete three-part short topology makes
the call window strict: January and candidate both emit
`add esp, 0x18; mov ebx, eax; cmp bx, -1`, with no later sign extension.
HCEA independently corroborates the three short types, but January code is the
authority.
## Frozen best candidate
| Measurement | January | Best ordinary-C candidate |
| --- | ---: | ---: |
| Meaningful / padded bytes | 533 / 544 | 533 / 544 |
| Relocations | 19 | 19 |
| Instructions | 164 | 164 |
| Normalized SHA-256 | `ddd3ed044dc0de47ecb6ba83ac683372ff87b3cb7d8fee318d20da5467c39ed8` | `f6c5eb3588f3efccd66611ba0c133b20dc3fb449fad4894f3022b8e9c6f04bd0` |
The immutable best scratch source snapshot is
`scratch/_gate_26956.c`, SHA-256
`d136beeae2cbe62fa65e46cfc3c16777cd8ae18ad70d2a51977d96feacf6199a`.
Its phase-local object is `scratch/_gate_26956.obj`, SHA-256
`0ae37faec0fc7446e23ffae6ad373a05a9dec640e0b486ae6c75de768afb41cd`.
The January split object is SHA-256
`82fd7ef88e212ebca0a7afe5e7eae37eef82054020988046090774865e5bb135`.
Raw candidate COFF identity is phase-local because debug records contain the
output path; the normalized function hashes are the reproducible identities.
Every relocation address, type, symbolic destination, and addend agrees. Code
before and after the pursuit-copy window realigns exactly. January uses one
compact source `lea`, loads all three point dwords, then schedules the stores
among the independent surface, cluster, and enable operations. VC7 lowers
the best aggregate source with the same compact `lea` but interleaves each
point load and store.
## New compiler probes
Twenty-five production-neutral variants were compiled from the immutable
snapshot. They cover ordinary inline pointer/value aggregate helpers,
`const` variants, by-value and return-by-value helpers, scalar/array/union
initializers, comma declarations and expressions, branch lifetimes, enclosing
aggregate layouts, and supported alias qualifiers. They add the following
facts to the historical E01-E20 matrix:
- aggregate, value-identity, pointer-local, and `const` forms all canonicalize
byte-for-byte to the `f6c5eb35...` best candidate;
- an inline helper taking a `real_point3d` by value and spelling scalar stores
does create January's compact `lea; load x; load y; load z` prefix, but VC7
also materializes a 12-byte argument temporary, shifts the frame, duplicates
stores, and diverges globally. It remains 544/19 but has normalized hash
`3169a4b10565db618e9129673a3ac294cd6c0103cc23345879cde972f58eb6cf`
and raw object SHA-256
`3c7338bd4dfd682a40fe591a937580be7ddc81674981c570e010a003c65030bb`;
- scalar return-by-value helpers remain out of line at 576 bytes / 20
relocations;
- a named enclosing target subobject rounds from 22 to 24 bytes, shifts every
following offset, and yields a nonviable 544/19 object with raw SHA-256
`dfde9276e1a93f833186af26bd492ff1e62f09fd7ffef490fc1c2fccc4e343d2`;
- XDK 3911 rejects `__restrict`, so this compiler supplies no supported
non-alias qualifier to test;
- the January PDB module stream for `action_uncover.obj` is only 124 bytes of
object/compiler records and contains no local-variable records.
The research harness is
`scratch/uncover_codegen_probe_subagent.py`, SHA-256
`176ae1e683a25e6850f75fb98fbae48b81fef7bb4dbee41afb010f371e19d66a`.
Scratch artifacts are evidence only and are not tracked or published.
## Public-source provenance
The user-supplied repositories were searched at their current public heads and
through relevant history.
- `punpckhdq/halo` is the same build-2342 project, but commit
`fc47f9a15a0aea7f0081394c4d66db5c68ab5323` and all 141 reachable commits
contain only the original `action_uncover.c` symbol/string stub.
- HCEA commit `570c83fd9c365dad6f2a3e7041705d5b84c7847c` contains the strongest typed
cross-build reconstruction. Its own comments identify the body as a manual
disassembly recovery after decompiler failure; it is not lost Bungie C.
- Stian/pastudan target retail Xbox `01.10.12.2276`, not January debug build
2342. Their bodies are Ghidra/LLM raw-pointer lifts. The two historical
enable/point statement orders are non-admissible and are already subsumed by
the measured scalar and field-order families.
- Baboon, demon, nimbus, halopc-restored, and xboxrecomp contain no January
function source or compiler-spelling record. Xboxrecomp pull request 5 is
useful runtime/recompilation infrastructure, but adds no source-provenance
control for this scheduler.
No authentic January/Bungie source or new admissible defined-C spelling was
found.
## Disposition
The production source is intentionally unchanged: the eight established exact
functions remain, `_action_uncover_perform` remains declaration-only, and the
two perform-owned strings remain absent. This preserves the atomic/fail-closed
boundary and all house rules.
Do not repeat the historical aggregate, scalar, alias, helper, field-order,
scope, barrier, representation-copy, or E01-E20 families, nor the 25 variants
above. Reopen only for authentic January source/local records, a same-compiler
strict-exact C donor, or a genuinely new defined-C lifetime dependency that
retains the compact base `lea` and load-all/store-all schedule without a new
temporary, frame change, relocation, or forbidden compiler control.
@@ -1,227 +0,0 @@
# `action_uncover.obj` Jonas recovery checkpoint
## Scope and provenance
This is a bounded partial recovery on authoritative integration commit
`2ef097537488b0c71589004a0047dc9a8c02b3e1`. The donor was the clean
`action_uncover.c` working-copy artifact in
`work/action-search-closeout-20260801` at
`10a2244a0d3e6a70b5945995700ff9d9b219dc92`. Its Git blob identity was
verified before transplant as
`a6c042c1d124b7bd6148ca1b3b34d26244f887db`.
Only `source/ai/action_uncover.c` is transplanted and adapted. No shared
header, configuration, semantic ledger, park entry, status label, existing
object log, or Claude-authored Markdown changes. The unit remains
`NonMatching`.
## Frozen-header compatibility bridge
The donor's dirty worktree also contained an unrelated `ai_debug.h` edit
that split `__unknown3C[105]` into `__unknown3C[104]` plus the named
`print_uncovering` Boolean. That header edit is not imported.
The local source instead uses the read-only
`ACTION_UNCOVER_DEBUG_PRINTING_ENABLED()` accessor over the already declared
`ai_debug.__unknown3C[104]` subobject. The frozen header declares that storage
as `char[105]`; the accessor reads the actual one-byte character subobject and
uses it only as a truth value, so it does not depend on signedness. A
zero-emission compile-time assertion requires the member identity to remain at
`+0xA4`. The independent
`hs_globals_external.c` registration names that same address
`ai_print_uncovering`. There is no cast, pointer arithmetic, write, union
overlay, or helper body.
A controlled A/B XDK build of the original named-field donor and the
frozen-header bridge proved the bridge code-neutral before the nonexact body
was removed. The final declaration-only build was then independently replayed:
all eight retained runtime function sections remain identical to the target in
size, normalized bytes, and relocations. The bridge therefore changes only
source/debug description, not the credited runtime code or data.
## Strict code result
The final forced XDK 3911 measurement reaches the fail-closed donor boundary:
8/9 strict-exact target functions, 2,054 meaningful bytes, 2,112 padded
bytes, and 71 relocations. Every credited function is exact in padded size,
normalized bytes, relocation count, relocation address/type, symbolic
destination, and addend.
| Function | Meaningful | Padded | Relocations | Normalized SHA-256 |
| --- | ---: | ---: | ---: | --- |
| `_action_uncover_setup_target` | 121 | 128 | 7 | `d2a5512fda5465b171d041acc0a9f229d8923ecbd07f095eb88bf7aebf4f7793` |
| `_action_uncover_setup_pursuit` | 247 | 256 | 10 | `9bb3ea855bc4ffb388e7ed9ec451eb2b63944b724094b75ffd87396b498721f2` |
| `_action_uncover_control` | 365 | 368 | 5 | `ac833f1fbfe0375f3e11ebd1132430a2c2b6ded9e21549c2d0f9fe09dc231e8d` |
| `_action_uncover_flush_position_indices` | 49 | 64 | 2 | `d32f3d33556ecc4034d286cc8dcaef9cbdc100164d9882af21f6eb706b8c8b9a` |
| `_action_uncover_flush_structure_indices` | 39 | 48 | 2 | `828e1700de76a884cac45684d4e9eb16164aac1d8554ab01f9491b715d870b62` |
| `_action_uncover_modify_color` | 102 | 112 | 4 | `c4db7ebcc9c18e628518139c9c96e35ada43909042d81c6b990e6e6342b659a4` |
| `_action_uncover_begin` | 367 | 368 | 18 | `eb0c3080244d12fedb171518d45dee6d575c7cb3c9f1d9f84a26e88c350aec9a` |
| `_action_uncover_update` | 764 | 768 | 23 | `47b5885aad0b232ad22cdee9177393a8fd77ac16931ae1c28e9a131dd1ee9356` |
The source also makes VC7 emit the header-inline
`vector_from_points3d`, `magnitude_squared3d`, and
`distance_squared3d` COMDATs. They are candidate-owned folded copies, not
target functions in this translation unit, and receive zero function credit.
## Fail-closed residual
`_action_uncover_perform` is deliberately absent from the candidate object.
The source retains only its public prototype. The final candidate symbol
table and `_action_uncover_update` relocation inventory contain no perform
symbol or relocation; no section, body, helper, or anchor is emitted. There
is no perform code credit.
For provenance, the rejected donor body measured 533 meaningful / 544 padded
bytes with 19 relocations. Its normalized SHA-256 was
`9c05cfd15a565d4fe76c1dca3670578a42f95173100d3ab91b17cba46fd453cf`,
against target
`ddd3ed044dc0de47ecb6ba83ac683372ff87b3cb7d8fee318d20da5467c39ed8`.
That nonexact body and its exclusively owned strings are not retained.
The donor already established the two residual scheduling windows: the
12-byte pursuit-position copy interleaves loads and stores instead of loading
all three dwords first, and the post-select `add esp, 0x18` / `mov ebx,
eax` order is exchanged. No tuning is performed after reproducing this
measured donor subset.
## RDATA and ownership boundary
Ordinary objdiff reports 82.608696% fuzzy RDATA similarity, but no
`matched_data` value because the aggregate target boundary is incomplete.
The campaign therefore receives 0/304 ordinary data credit. Independent
owner-by-owner comparison proves eleven physically owned target COMDATs,
218 bytes total, exact in owner identity, logical size, flags/alignment,
payload, and zero-relocation shape:
| Literal/constant owner | Bytes |
| --- | ---: |
| source path | 35 |
| begin-uncover format | 63 |
| `pursuit` | 8 |
| `target` | 7 |
| `un` | 3 |
| uncover-done format | 24 |
| unknown-reason text | 17 |
| location-inspected text | 19 |
| persistent-timer format | 20 |
| timer-finished format | 18 |
| `60.0f` | 4 |
Another eleven target bytes are the folded
`"state_data"` assertion-expression COMDAT. The split action object records
that owner undefined, while VC7 emits the normal local select-any copy.
Direct comparison with `cachebeta.exe` at the independently recorded symbol
file offset 2,371,308 proves both sides are the same eleven bytes with
SHA-256
`8eeb47459ccd3b18728d86838a1447f42b0207e2ef5ff5996fdaefa90d849878`.
This is ownership attribution, not a semantic-data exception. The strict
independently proven retained-data boundary is therefore 229/304 bytes.
The two target-only perform strings total the missing 75 bytes: 31 bytes for
the inspected-pursuit message and 44 bytes for the unable-to-see-target
message. They disappear with the rejected body. Two candidate-only folded
COMDAT definitions total five bytes: the empty string and `30.0f`. They have
no target-owned section in this split object and receive zero data credit.
## Source and policy audit
The retained reconstruction is readable typed C. Parameters are vertically
formatted and every function has an explicit return. It introduces no
assembly, volatile scheduling device, force-inline control, optimizer pragma,
barrier, undefined aliasing, pointer pun, raw-address arithmetic, data write
through the compatibility bridge, byte-forcing expression, or synthetic
anchor.
The frozen `vehicles`, `ai_debug`, `ai_script`, `actions`, and
`units` source/config/log surfaces are unchanged from `2ef09753`.
## Validation
- full `halobetacache_build` and `libcmt_build`: pass in the 572-edge
consolidated build;
- semantic audit: 470 units, 3,948 functions evaluated, 3,808 semantic exact,
3,869 accepted exact, zero unit errors;
- campaign progress: 375/833 objects, 3,856/11,060 functions,
467,019/2,198,102 code bytes, and 1,846,712/4,176,062 data bytes;
- Halo progress: 273/468 objects, 3,689/7,574 functions,
454,105/1,770,166 code bytes, and 1,841,520/3,923,451 data bytes;
- object admission: zero candidates, zero revocations; only the inherited
unrelated `shell_xbox` contradiction remains;
- parked-function audit: 3 active, 0 stale, 0 invalid;
- tooling suite: 179/179 tests pass;
- configuration remains `NonMatching`; no semantic or parked exception is
added.
Reproducibility identities before the final commit:
- adapted source Git blob:
`a6b3cd4af7259f2268f762f4135ffb48a855e63d`;
- adapted source SHA-256:
`57735f1cf03c852c642a070c35f0eda684ff7ec0d3d39c38589cf601c209e3cb`;
- January target object SHA-256:
`82fd7ef88e212ebca0a7afe5e7eae37eef82054020988046090774865e5bb135`.
The raw candidate COFF hash is intentionally omitted because the timestamped
object container changes across forced builds; the per-function normalized
hashes above are the reproducible code identities.
A final committed-state snapshot and forced same-path rebuild/check are
performed after the local checkpoint commit. Nothing is pushed.
## HCEA short-width follow-up (2026-08-26)
The user-supplied HCEA corpus was refreshed at commit
`570c83fd9c365dad6f2a3e7041705d5b84c7847c`. Its
`src/blam/ai/action_uncover_perform.c` adds two source facts that were absent
from the earlier PDB-only packet: the later-build selector is declared to
return `int16_t`, its receiving local is `int16_t`, and the pursuit point is
spelled as three indexed `n[]` assignments. These are cross-build hypotheses;
January code remains authoritative.
The clean pre-wave regression manifest is
`build/audit/action_uncover_short_return_baseline_20260826.json`, SHA-256
`6fefc18d013dd0bd3250b91a0ade399fd2c7a120b058cee97377d3a17508acba`.
The preserved preimage object has SHA-256
`c9af8e7d7d762362e93db4a0a15d380d8f04ab0f64039755a23bab5d97569999`.
All candidate source packets passed VC7 `/Zs` before their ordinary compile.
Three evidence-separated measurements were made:
| Candidate | Source SHA-256 | Object SHA-256 | Normalized perform SHA-256 | Result |
| --- | --- | --- | --- | --- |
| HCEA scalar point copy, short selector return and local | `5e44b5ceeda63c464f4c256582a67a944dd0f0b0edc2c1d9f1135ee2687779d0` | `9607a4d247ce07b7bf7cf76cc64948eba677deef98f19a505fb540ab5f04d509` | `1a81f8957d0af644dc2fb00a9b59b28f127819bed8cdffdb0d7edcf459736a54` | rejected |
| Aggregate point copy, short selector return and local | `a86776739746ae14fca69bd4cc90729583e7134cbfbb5b3f53200285b414a6bd` | `597fe15cb624f8a1c06bf0785136b7d2e8644a9c5f4936b34f4a27f15d105ac5` | `1ad7a09e123a93efdc1ea16451b49765676871fb5b11e0ac2ffd2a215fe63e67` | rejected |
| Aggregate point copy, January `long` selector return, short local | `5d3c695fb508e98af777e90ec3144c5c8abb2d247033ce373d5bb346bc448aaa` | `24be90ad9b6f332a3e848b5603447f86f05172560f66ec4300a1cdd5a721083d` | `300bba8c0c0ea302c17b22c44e2cbb8da3ad2e8031ee7cb9546e84e4faf5fa56` | rejected |
The first artifact proves the indexed scalar spelling is not January's
source shape. It replaces the compact source-base `lea` with three full
displacement loads, adds four bytes before the selector call, and shifts that
relocation from `+262` to `+266`.
The two aggregate artifacts restore the 544-byte envelope and every
relocation through the selector call. Narrowing the receiving local does
produce January's desired post-call order exactly:
`add esp, 0x18; mov ebx, eax`. It also requires a three-byte sign extension
before `actor_change_firing_position`, shifting only that final relocation
from January `+504` to candidate `+507`. Restoring the selector declaration to
`long` does not remove that consumer-side extension. Thus HCEA's short width
is later-build drift, not January's exact ABI.
Both aggregate artifacts retain the other known residual: January loads all
three pursuit-point dwords through one compact base `lea` before any of the
three destination stores, while VC7 interleaves each load and store. The
fresh HCEA source therefore closes the provenance question but supplies no
legal-C spelling that satisfies both scheduling windows simultaneously. The
prior aggregate, scalar-lifetime, representation-copy, alias, helper, field-
order, and scope do-not-repeat list remains in force.
No rejected perform body or perform-owned data is retained. The source was
restored byte-for-byte to its pre-wave Git state and rebuilt normally. The
restored object SHA-256 is
`558369011d594c5e95ef4b43db52e15ebceddae92b87fa88a95b213a75b4f70d`.
The frozen regression gate passes with all eight accepted functions under
`still_exact`, no changed nonexact function, no failure, and no warning.
`action_uncover.obj` remains 8/9 exact and incomplete. Reopen only for a new
January-side source/provenance record or a defined-C control that produces the
compact load-all/store-all point copy without introducing the final
sign-extension tradeoff.
@@ -1,32 +0,0 @@
# action_uncover.obj — opus5 150K lane, wave w2 (2026-09-14)
## Baseline and final
| gate | result |
| --- | --- |
| baseline real file | `== exact 8 residual 1 unwritten 0`, guard passes |
| final real file (unchanged) | `== exact 8 residual 1 unwritten 0`, guard passes, park drift 0, fake scan 0 leads |
`git diff --stat -- source/ai/action_uncover.c` is empty. No credit is claimed.
## `_action_uncover_perform` (parked, instruction-scheduling): SKIPPED-EXHAUSTED
Target 544 / 19 / `ddd3ed044dc0de47`; candidate 544 / 19 / `f6c5eb3588f3efcc`.
The residual was re-disassembled. The only differing window is +0xa2..+0xd3:
- January: `lea edx,[esi+0xb0]`, then loads x/y/z into eax/ecx/edx, then stores x, (surface load), y, (cluster load), enable=1, z, surface, cluster.
- VC7: alternates each point load with its store.
No lever from brief section 9 applies:
- no call inside the window (store/call order);
- equal frame;
- no x87;
- no inline helper;
- one return;
- no accessor-binding difference (same compact lea).
The do-not-repeat lists already cover the remaining candidates: E01-E20, the 25 probe variants of 2026-08-28, and 45 defined-C families. No shape was compiled.
Reopen criterion (unchanged): authentic January source or local records, or a same-compiler donor for the load-all/store-all copy schedule.
@@ -1,94 +0,0 @@
# `action_vehicle.obj` evaluate-seat symbol reconciliation (2026-09-13)
## Scope and ruling
January target file offset `0x000094C0` is authentically
`action_vehicle_evaluate_seat`. `config/symbols.json` therefore maps the old
address placeholder `_code_000094c0` to
`_action_vehicle_evaluate_seat`. This is a symbol-identity correction only;
no function body, prototype, linkage, compiler flag, or source schedule changes
in this packet.
The corrected mapping makes the already reconstructed private caller
`action_vehicle_find_impromptu_seat` strict-exact. The evaluate-seat body
itself remains fuzzy and receives no exact credit.
## Same-build name evidence
The exact January/Halo CE atlas record is:
```text
{"off":"0x4094c0","name":"_action_vehicle_evaluate_seat","lib":"action_vehicle.obj","tier":"exact"}
```
It is record 53 of:
```text
research/halo-symbol-atlas/symbols/halo_ce/4cc87b45f721270392a96f1674ed2b5cd4a7bb4355faeab4531d1cf1884d9520.jsonl
```
The file's SHA-256 is
`851802ac508bbcf7cd2896c6cfddd1e0f81510b7b9ef9ea02f39f913c123578e`.
Subtracting the image base `0x400000` places the symbol at target file offset
`0x94C0`, exactly the `config/symbols.json` record being corrected. The
adjacent same-build records independently anchor the object topology:
- `0x4093B0`: `_action_vehicle_desirable`;
- `0x409890`: `_action_vehicle_find_destination`;
- `0x40A1B0`: `_action_vehicle_find_impromptu_seat`.
This direct same-build map, not a behavioral guess, authorizes the production
name and relocation identity.
## Corroboration and limits
Stian's commit `32a94ebf59066e34057e6e6bfe625d64c61b1be5` introduced the
same name after decoding the action-definition table and cross-referencing the
call graph. That is useful independent semantic analysis, but it is not
original-symbol evidence and was not used alone to admit this mapping.
The HCEA atlas also contains exact PDB records for
`action_vehicle_evaluate_seat`, including `0x83821698` in
`567b63bd5f1c5aed0f99b8bc2bb70c9be655d8f6df81d2cbb20c725b33fd6084.jsonl`.
Those records corroborate the spelling and role across builds; they are not
interchangeable January layout evidence.
The supplied `h1_tags/*_symbols.exe` images contribute no name proof here.
They contain references to missing external PDBs, zero COFF symbols, and no
embedded original function-name records. Analysis-generated Ghidra names are
not treated as Bungie symbols.
## Focused verification
Before target-map regeneration, the universal identity-only scan reports the
sole mismatch in the 304-byte caller:
```text
source/ai/action_vehicle::_action_vehicle_find_impromptu_seat
@0x0072 target _code_000094c0 + 0
ours _action_vehicle_evaluate_seat + 0
```
The ordinary focused gate reports 9 exact, 5 residual, and 3 unwritten
functions because its existing split target still carries the old placeholder.
Running the same gate with the authenticated in-memory alias
`action_vehicle_evaluate_seat=code_000094c0` proves the post-regeneration
identity result without disturbing the shared build directory:
```text
EXACT 304 _action_vehicle_find_impromptu_seat
residual 976 _code_000094c0 [size 960!=976, relocs 31!=30, sha]
== exact 10 residual 5 unwritten 2 (of 17 listed)
```
Thus the caller is strict-exact at 292 meaningful / 304 padded bytes with
three matching relocations after the map correction. The current semantic
ledger had already admitted those 292 meaningful bytes through its ordinary
normalized-code proof despite the unresolved callee spelling. This rename
upgrades that existing result to strict relocation identity; it must **not**
be booked again as a new 292-byte campaign gain.
The reconstructed evaluate-seat body is 960 padded bytes against January's
976 and has 31 relocations against 30. It remains a best-source fuzzy body,
earns zero exact bytes here, and must not be described as closed merely because
its original name is now known.
@@ -1,220 +0,0 @@
# ActionVehicle Fable independent intake (2026-09-06)
## Decision
The smallest house-rule-clean packet is **`_action_vehicle_control` only**.
Keep `_action_vehicle_begin` held.
The control packet is three production-source edits for a later owner trial:
1. Add the donor control body and its TU-local
`_action_vehicle_idle_look_type = 4` enum to
`source/ai/action_vehicle.c`.
2. Add `void action_vehicle_control(long actor_index);` to the genuine owner
header, `source/ai/actions.h`, under an `ACTION_VEHICLE.C` prototype block.
3. Remove the now-redundant consumer-local declaration of
`action_vehicle_control` from `source/ai/actions.c`.
Do not copy the whole donor file: it also contains the held begin body. No new
header include, symbol alias, macro, global, static helper, data owner, or
configuration change belongs in the control-only packet.
`_action_vehicle_begin` is held because its HCEA/prior-rejected contract
already had the same three semantics: reset the counter, store
`game_time_get()`, and copy `body_position`. Fable's only material source-shape
change is the `struct vehicle_state_data *state_data` alias. Exactness therefore
comes from a new lvalue topology, not new semantic provenance, and does not
satisfy the explicit 2026-08-21 reopen condition. If begin is ever lawfully
reopened, it additionally needs the genuine `#include "game/game.h"`; the
genuine public declaration of `game_time_get` is `source/game/game.h:171`.
## Why control is a genuine semantic reconciliation
The later-build HCEA source uses the wrong January priorities:
- lock-facing: `_primary_priority_exact_facing` (canonical value 2);
- path-present: `_primary_priority_facing` (canonical value 1).
The January target machine code instead writes 4 and 3. Canonical
`source/ai/actors.h:62-66` authentically names those values
`_primary_priority_locked_aiming` and `_primary_priority_aiming`. The Fable
control body used those two names in its literal first saved source, before any
candidate object existed. This is a code-proven semantic correction to the
HCEA contract, not an alias-only scheduling retry.
The remaining contract is also owner-backed:
- `actors.h:210-214` owns `_direction_specification_movement = 0` and
`_direction_specification_vector = 4`;
- `actors.h:265` owns `actor_get`, `actors.h:1322` owns `actor_data`, and
`actors.h:1157` owns the `actor_path_has_path` prototype
(`source/ai/actor_moving.c:310` defines it);
- `actions.h:246` owns `struct vehicle_state_data`, and its action-data union at
`actions.h:389` owns the `vehicle` arm;
- the schema has `lock_facing` at vehicle offset 44 and
`destination_facing` at offset 60. January reads actor offsets `0xC8` and
`0xD8`, fixing the vehicle arm base at `0x9C` and independently confirming
both fields;
- `actors.h:582` owns the short `idle_look_type`. The value 4 is present as a
TU-local action constant in both `source/ai/action_charge.c:127` and
`source/ai/action_avoid.c:35`; no shared idle-look enum exists.
The target control has 175 meaningful / 176 padded bytes and three
relocations: `DIR32 _actor_data` at `+4`, `REL32 _datum_get` at `+17`, and
`REL32 _actor_path_has_path` at `+89`. Its normalized SHA-256 is
`81612c85cf900b15c2de8ea6d52646f5727a96fbea5e2f13018e6f9613fcd293`.
The first code-producing Fable candidate and committed donor object are strict
equal on all bytes and relocation identity.
Both target and candidates define `_action_vehicle_control` as external
function symbols (`storage=2`, `type=0x20`, section-relative value 0). There is
no private/static implementation symbol to rename.
## Literal-first versus first-code-producing provenance
The Fable report's phrase "EXACT - first compile" is accurate only when
"compile" means the first code-producing compile:
- `scratch/_gate_16992.c`, created `2026-09-04T22:46:23.2697770Z`, is the
literal first saved source. It already contains the final state alias,
locked-aiming/aiming branches, direction choices, and tail stores, but uses
undeclared `_idle_look_combat`. No `_gate_16992.obj` is preserved.
- `scratch/_gate_35296.c`, created
`2026-09-04T22:46:38.1663797Z`, differs only by adding the TU-local
`_action_vehicle_idle_look_type = 4` declaration and renaming that one use.
`_gate_35296.obj` is the first preserved code-producing object, and control
is strict exact there.
- The parse/name repair is disclosed. It did not change control flow, field
access, priorities, call identity, or emitted runtime data, and is accepted
here as pre-code-generation semantic-name closure backed by the two sibling
owners above. It must not be described as literal-first-source exact.
- `_gate_35296.c` and the committed donor `source/ai/action_vehicle.c` are the
same Git blob, `0e89db5e7b3072e1ebb5220f7d5a436428b57d69`.
The earlier canonical ledger is not superseded silently. It records the
one-shot HCEA control miss as normalized SHA
`3f96cbf0cdc3a58d1013ac2d44df261acc0d3b68c37ebab639956ddfa1a046ad`
against the January hash above and requires new authenticated contract
evidence. The 4/3 priority correction is that new evidence for control. No
equivalent semantic delta exists for begin.
## Caller and owner closure
The complete current source search finds only one consumer:
- `source/ai/actions.c:764-765` declares control locally;
- `source/ai/actions.c:967` stores its address in the vehicle action callback
table.
Current base `actions.obj` carries one `DIR32` reference at `.rdata+536` to an
undefined external function symbol `_action_vehicle_control`; January split
`actions.obj` carries the same reference at `.rdata+536`. The corresponding
begin reference is `.rdata+524`, but begin remains out of scope. No other
current base or January split object contains either callback symbol.
`actions.c` already includes `actions.h`, so moving the control declaration to
the genuine header and removing the consumer-local declaration leaves the
same signature visible at the callback table. Adding a new `action_vehicle.h`
would be an artificial facade and is not admissible. Leaving only a C-local
declaration is likewise not owner closure.
Because `actions.h` is shared, a later trial must freeze and compare all of its
actual consumers plus `actions.obj` and `action_vehicle.obj`; this intake did
not query Ninja or compile anything.
## COFF delta
Against the current canonical base object, `_gate_35296.obj`,
`ng_action_vehicle.obj`, and the donor final object all add exactly two named
external code owners: begin and control. For the proposed subset, admit only
control.
All seven inherited exact owners remain exact:
`_action_vehicle_update`, `_action_vehicle_flush_structure_indices`,
`_point_from_line2d`, `_distance_squared2d`, `_distance2d`,
`_negate_vector2d`, and `_object_get_bounding_sphere`.
The candidate has no COMMON symbols, BSS section, new writable/read-only data
owner, or named static code helper. Its only additional undefineds are
`_actor_path_has_path` and `_game_time_get`; the latter belongs solely to the
held begin body. Section-symbol `.text` entries with storage 3 are compiler
section bookkeeping, not private helpers. The local enum emits no symbol or
storage.
## Pinned artifacts
Canonical frontier: commit
`c2561af50c7fda9c5ea3326b35b4ba10f90d17f5`.
- January target:
`build/split/source/ai/action_vehicle.obj`, 11,826 bytes,
SHA-256 `49F00A0115CECF578E51102FC33F665F876A4FAFCB78EE3B53520545406EF769`.
- Current canonical candidate baseline:
`build/base/source/ai/action_vehicle.obj`, 3,084 bytes,
SHA-256 `84FE91F3F197946CA8EC1EED569AFBF77774B44B6F2CEAE4A8540396979E4120`.
- Current callback consumer:
`build/base/source/ai/actions.obj`, 56,841 bytes,
SHA-256 `05FB549A9F707E1E97EE9A44D11CF9AE7AC534752A00ECF22652C60C3374D9D4`;
January `build/split/source/ai/actions.obj`, 53,049 bytes,
SHA-256 `624BB4506ADFF68E9888A20E44042F28EC7F7E4FDC33115C64C25BEB1270B5B5`.
- Canonical source:
`source/ai/action_vehicle.c`, SHA-256
`22560B33AD7A0250E1ADBD5F500C52DF4102FEAC97E2A280E1E2D1F29428529F`,
Git blob `6f689f90311fc56f1f3c0d58c593df81390efdca`.
- Canonical owner header:
`source/ai/actions.h`, SHA-256
`EA1BEC48DFCC5BDAD4DC01137E8C9D4A1F6AA878EE6F3EAEF4D0EF13E64AEE62`,
Git blob `5df907fab02c19fd14904ede814923362e2309f5`.
- Canonical consumer source:
`source/ai/actions.c`, SHA-256
`F6347EB2573A36F12CF0C9253DA66504441B8743269E8847FD3866770571D7EC`,
Git blob `b1f6be227e232de1a7a5dec92710b5dd8aaea3e6`.
- Canonical schema/API source:
`source/ai/actors.h`, SHA-256
`411A70414C9ED5605A6AB01D14503D7E4B82F2B04BE1D12865CD6D96F45D0B8A`,
Git blob `ea715dbedf141721eb4c1bf4585bab5cb7c905be`.
- Main prior ledger:
`docs/object_matching_logs/action_vehicle_obj_jonas_one_leaf_20260821.md`,
SHA-256 `C01350FD8E5C4CBAF48026405D7480E2297FAB4CC307BA0D322693E931627372`.
- HCEA begin source:
`C:/Users/isabe/Documents/Codex/2026-07-13/i-w/research-cache/halocea-full-blobs-20260830/src/blam/ai/action_vehicle_begin.c`,
SHA-256 `4820BFAFAACD281D4F1DBA31312158B158AF55AAD5DAFF4D04E98D247763A3B0`.
- HCEA control source:
`C:/Users/isabe/Documents/Codex/2026-07-13/i-w/research-cache/halocea-full-blobs-20260830/src/blam/ai/action_vehicle_control.c`,
SHA-256 `96F8D95AA428D7AA9F1535A4C2847B8CDA6D97F8C7467E02B883B2C1924C3716`.
Donor frontier: commit
`f9dd42218b3325e1b3a63d7860e5309f69fd3d8e`, parent
`96086c9c4c187dd539af13bdaa9252d6e46ad8f6`.
- Donor report: `scratch/reports/action_vehicle.md`, SHA-256
`36D66D460BD2A3F7E0D7C1C623F76A580BA012B22E979AA0A8226E3E3A4043C6`.
- Donor facts: `scratch/facts/action_vehicle.md`, SHA-256
`24D06EEAC322DE860653F348308B302404CEEF1B0B9DD31C4C7D6A927DAC052F`.
- Literal first source: `scratch/_gate_16992.c`, 4,808 bytes, SHA-256
`5300FDEB147AA82B39298432663C63F4D7D8E12452B36B386524F7849F7D0F8D`,
Git blob `ea342b9e03c72d12584831d6fef4f2a95d3c8f99`.
- First code-producing source: `scratch/_gate_35296.c`, 4,869 bytes,
SHA-256 `F8F6B69F62B253478B494FE68AF1DABBE987175660E0C41C976EA23F54266F99`,
Git blob `0e89db5e7b3072e1ebb5220f7d5a436428b57d69`.
- First code-producing object: `scratch/_gate_35296.obj`, 3,651 bytes,
SHA-256 `3AAC2DB9C10B2A07E9614358D93F416E5C1FBC7B8EAAD75EA355BF10F6C8E66B`.
- Saved later natural object: `scratch/ng_action_vehicle.obj`, 3,651 bytes,
SHA-256 `702BFF861ACA3E3D961157DCDBD7D7196A812D5F5EEA3CDFD5A06F9EDD721431`.
- Committed donor source: `source/ai/action_vehicle.c`, SHA-256
`3E5997DDB9769E1E80682B21E117986BACC2DD1BFD238877ACA392F3A8990A7F`,
Git blob `0e89db5e7b3072e1ebb5220f7d5a436428b57d69`.
- Donor final object: `build/base/source/ai/action_vehicle.obj`, 3,667 bytes,
SHA-256 `0736AE24E228A91848D26B73F8FE86D72EC9B7ACEA9FF42ACA9337D349E4D207`.
The differing raw candidate-object hashes are explained by producer/debug
record paths and timestamps; the hardened function comparison is strict equal
for both proposed functions and reports zero inherited losses.
## Scope statement
This was read-only over canonical production files and the donor. No source,
header, configuration, object, Ninja state, or donor file was changed; no
compile, configure, Ninja, or commit command was run. This report is the only
new file.
@@ -1,139 +0,0 @@
# ActionVehicle: Fable control reconciliation (2026-09-06)
## Scope and verified runtime gate
The one genuine `action_vehicle_control` correction matches on its first
canonical compile and in the ordinary Ninja output. It adds **175 meaningful
/ 176 padded exact code bytes** and one function. ActionVehicle advances
**7 -> 8 / 17 exact**, with zero residual and nine unwritten functions; it is
not a complete object. `action_vehicle_begin` is deliberately not imported.
The local strict sweep is **6,256 exact / 8,245**, gaining precisely this
function with no inherited exact losses. Official local totals are
**916,726 / 2,198,102 meaningful exact code bytes**, **6,210 / 11,060 credited
functions**, **391 / 833 Matching objects**, and unchanged **2,061,020 /
4,176,062 data bytes**. The preceding published geosphere commit is
`b90402d960a75d312f2a3895c43850e9cca4ccb8` on both authorized refs.
Independent runtime checks now pass. These packet-local numbers precede the
independent HUD helper's four meaningful bytes and are not alone a claim of
publication. The joint local checkpoint is 916,730 meaningful bytes.
## Why this is a legitimate contract correction
The prior 2026-08-21 HCEA-based control attempt used the canonical facing
priorities 2/1. January writes **4/3**, independently named in `actors.h` as
`_primary_priority_locked_aiming` and `_primary_priority_aiming`. Fable's
literal first saved source already contains this semantic correction.
The only later pre-emission repair supplies a semantic TU-local name for
the proven idle-look value 4; the first preserved code-producing object is
exact. That parse/name correction is disclosed, not misrepresented as
literal-first-source exactness. Root made no post-compile source variants.
Begin has no analogous contract correction: its stronger donor shape only
introduces a local state pointer over already-correct reset/time/copy logic.
Its old source-shape boundary therefore remains held. Do not import the
whole donor source or count both claimed 256 padded bytes.
The independent source/history/COFF intake is
`action_vehicle_obj_fable_control_intake_20260906.md`. Root read all seven
prior ActionVehicle ledgers, the donor report/facts and complete control
diff, the HCEA control body, and the genuine current type/enum/API owners.
The user-authorized Fable donor remains read-only at
`C:/halo-worktrees/fable-50k-small-families-20260904`, tip
`f9dd42218b3325e1b3a63d7860e5309f69fd3d8e`.
## Genuine owner boundary and full consumer verification
The sole prerequisite moves the unchanged public `action_vehicle_control`
prototype from the consuming `source/ai/actions.c` to the genuine owning
`source/ai/actions.h`, under its normal source-family section. The existing
header owns `vehicle_state_data`; `actors.h` already supplies typed actor
access, priorities, direction kinds and the real `actor_path_has_path` API.
No new facade header, duplicate C-local prototype, global, alias, schema,
compiler option or configuration exception is introduced.
Before that edit root captured all **47 actual Ninja consumers**, both
sources, the header, January target, configuration and all 572 base hashes.
The schema-only build preserves all 6,255 exact functions. Across the full
runtime closure, **3,107 sections are unchanged and 53 differ only in
compiler-local-label spelling**. There is no lost/changed runtime content,
new COMMON storage, or new `point_from_line3d` definition/reference.
After adding the body, the same inherited 3,160 runtime sections survive.
ActionVehicle alone adds the intended control section. It adds no helper,
data or BSS owner. Existing helper copies are unchanged, not new progress.
The genuine Actions callback table retains its `DIR32` control reference
at `.rdata+536`; no artificial caller keeps the body alive.
Source uses `actor_get`, authentic named fields and enums, a real vector
copy, explicit `FALSE` flags and terminal return, with one parameter per
line. The locked branch copies the saved facing and skips path lookup;
the unlocked branch chooses aiming/movement only when the real path API
succeeds. All three branches set idle look and clear the six genuine
shoot/movement order flags. No assembly, punning, raw offset, manual
helper expansion, register/volatile steering or fake matching is introduced.
## Exact and frozen identity
Control target/first/actual agree on normalized SHA-256
`81612c85cf900b15c2de8ea6d52646f5727a96fbea5e2f13018e6f9613fcd293`.
All three ordered relocations agree: `DIR32 _actor_data` at +4,
`REL32 _datum_get` at +17, `REL32 _actor_path_has_path` at +89.
The target symbol is an external cdecl public function, not a private
address-named body requiring an invented ABI.
All paths below are canonical-relative:
| Artifact | SHA-256 |
| --- | --- |
| `scratch/action-vehicle-fable-control-before-20260906.json` | `fa933a6deb8788e398a06037db7730cace93f8ab47a7efae636b012ae97430d4` |
| `scratch/action-vehicle-fable-control-schema-20260906.json` | `6ddf088f8265e4fc707d16a76711df93682d5406965628a4a85bbb22b9462289` |
| `scratch/action-vehicle-fable-control-final-20260906.json` | `081017c7ca9d82bf5b240e64a27115ec3d187901a7f2b04f77e291182b8ddf7b` |
| frozen January object | `49f00a0115cecf578e51102fc33f665f876a4fafcb78ee3b53520545406ef769` |
| `scratch/action-vehicle-fable-control-first-20260906.obj` | `6bc330954c197c8a5dc54c3ed269e39b5d894edafc4d0a4f265b235686b46c2f` |
| frozen actual object | `0f80fc38c93779cce5e03eceea3dffa9788386a497300ac6c18e307c66c4f82c` |
| frozen ActionVehicle source | `a45f810e182ca07d962da22deb8f9ea0063382650205248d74f66530f7a7725b` |
Capture: `scratch/action_vehicle_fable_control_capture_20260906.py`.
Full runtime comparisons:
`scratch/action-vehicle-fable-control-{schema,final}-runtime-20260906/summary.json`.
Final stable snapshot:
`scratch/action-vehicle-fable-control-final-stable-20260906.json`.
Build/gate logs: `scratch/action-vehicle-fable-control-{first,schema-build,final-build}-20260906.log`.
## Independent execution
Root reviewed the complete public oracle and its 24 fixture-free tests, then
replayed it against the frozen January, first reconciled candidate and actual
owner-correct object. All **18 cases pass for each object**, with identical
trace SHA-256
`87fe79ca103e97d9c177ba4def03ab82e8e91b2ce4c9f41253b761cba9341ed7`.
All four pinned real-byte mutants are detected by both semantic checks and
target/candidate comparison.
The oracle checks the entire seeded 0x800-byte actor-memory envelope, precise
write order/values, genuine API call order and forwarded handle bits, caller
return/argument window, stack balance, clear DF and preserved nonvolatile
registers. Locked facing exercises raw vector-word copies, including unusual
bit patterns; unlocked paths vary the boolean AL byte while poisoning upper
EAX and volatile registers. There is no floating-point arithmetic in this body.
The envelope is a test boundary, not a `sizeof(actor_datum)` claim. Arbitrary
and NONE-shaped handles are forwarding tests with the datum stub returning
the modeled owner; they do not claim those handles are valid engine data.
The void function's volatile registers are differential observations only.
Only genuine `datum_get` and `actor_path_has_path` dependencies are stubbed.
| Artifact | SHA-256 |
| --- | --- |
| `tools/audit/action_vehicle_control_runtime_differential.py` | `1f6a27acb013afe57f9ebf43a0020e04fb0720dd56f07653e21dd61ce3af2f79` |
| `tools/test_action_vehicle_control_runtime_differential.py` | `cc4f093dec9026fbff5c4495f70ccf3e67915bbed985f46d5952b68478df7327` |
| `scratch/action-vehicle-control-public-runtime-root-20260906.json` | `71a50adc3ddea8014a8bf19dc538d970966b47534485db21b017f3e60d455b34` |
The subsequent ModelAnimations owner-only experiment is completely restored:
all 1,523 sections across 13 consumers match the pre-trial state, including
Units. No graph implementation was compiled. Its rejected boundary is in
`model_animations_obj_fable_node_owner_trial_rejected_20260906.md`.
Final joint publication checks are recorded in the reconciliation backlog;
remote refs must be verified against the resulting commit, not this ledger.
@@ -1,128 +0,0 @@
# Action-vehicle bounding-sphere recovery (2026-08-27)
## Result
The first and only ordinary code-producing candidate makes
`_object_get_bounding_sphere` strict exact in
`source/ai/action_vehicle.obj`. The object advances from 6/17 to 7/17 exact
functions and from 165/4,909 to 294/4,909 meaningful code bytes. It remains
`NonMatching`; no data, completion-label, semantic-exception, or
parked-function credit is claimed.
All six inherited exact owners remain exact: `_action_vehicle_update`,
`_action_vehicle_flush_structure_indices`, `_point_from_line2d`,
`_distance_squared2d`, `_distance2d`, and `_negate_vector2d`.
## Frozen target and provenance evidence
January defines an external cdecl
`void(long, real_point3d *, real *)` owner with 129 meaningful and 144 padded
bytes. It retrieves a typed all-mask object, asserts non-null `center` and
`radius` outputs with canonical `objects.h` line metadata 535 and 536, copies
the named three-float bounding-sphere center, copies the radius, and returns.
Its nine relocations are fully fixed: `REL32` to
`_object_get_and_verify_type`; two references to the canonical objects-header
path literal; references to the `center` and `radius` assertion literals; and
two ordered `REL32` pairs to `_display_assert` and `_system_exit`.
The canonical inline implementation in `source/objects/objects.h`
independently supplies the exact signature, typed `object_get` access path,
assertion strings and line numbers, named fields, copy order, and explicit
terminal return. The HCEA later-build source at commit
`570c83fd9c365dad6f2a3e7041705d5b84c7847c` independently corroborates the
typed object datum and sphere-field semantics; its source blob is
`3bed3193ca7ec40b5f6c81955fc1452b7b5daaf9`. HCEA omits January's assertions
and is semantic/type provenance only; January PC COFF remains the sole padded
byte and relocation authority.
The complete Claude campaign tree, Stian sources and verification metadata,
all current object ledgers, and all repository revisions were searched before
emission. None contains a prior action-vehicle candidate or fixed-point record
for this owner. The existing global and HUD copies authenticate the shared
contract but do not claim this translation unit's COMDAT ownership.
The adjacent `_action_vehicle_begin` and `_action_vehicle_control` owners were
excluded before emission because the 2026-08-21 action-vehicle ledger already
preserves their natural HCEA-backed misses and requires new January evidence
before any retry.
## Retained typed source and house-rule audit
A translation-unit-local macro rename preserves the canonical inline owner
under a private name while allowing January's external COMDAT to be defined
from the same typed body:
```c
#define object_get_bounding_sphere object_get_bounding_sphere_inline
#include "actors.h"
#undef object_get_bounding_sphere
void object_get_bounding_sphere(
long object_index,
real_point3d *center,
real *radius)
{
struct object_datum *object = object_get(object_index);
match_assert("..\\objects\\objects.h", 535, center);
match_assert("..\\objects\\objects.h", 536, radius);
*center = object->object.bounding_sphere_center;
*radius = object->object.bounding_sphere_radius;
return;
}
```
Every parameter is on its own line, the C89 declaration precedes statements,
the canonical typed object-access macro replaces any raw access cast, the body
uses named fields, and the void function ends with an explicit return. It
contains no assembly, `register`, `volatile`, optimizer barrier, pragma,
intrinsic, attribute, raw address, byte-offset access, pointer/integer
reconstruction, pointer or union pun, undefined behavior, synthetic anchor,
object patch, comparator exception, or byte forcing.
## Immutable artifacts and strict comparison
- Base commit: `6d23d0f7bc976fcd24480411ee6e4878a758d8df`.
- Pre-wave source blob: `96eb18be20f7f1aa3e40e65063e9d65f5b57650c`.
- Frozen retained source blob: `6f689f90311fc56f1f3c0d58c593df81390efdca`.
- January split object SHA-256:
`49F00A0115CECF578E51102FC33F665F876A4FAFCB78EE3B53520545406EF769`.
- Baseline candidate object SHA-256:
`0B473CDE87C91DB8E453A108F777B1138C5556CC44B31303F509AEA8B634B048`.
- First-shot object SHA-256:
`4A44DE94FA7BC403290B54809F4DC08EAD97D2DC6DC50EC57724626599A96CE4`.
- Compiler: Microsoft XDK 3911 CL 13.00.9254.1 with the unchanged natural
`/O2 /Oy- /DDEBUG /Dxbox` flags and include graph.
The hardened comparator proves all 144 padded bytes, all nine relocation
addresses, types, destinations, addends, and ownership exact. Target and
candidate share normalized SHA-256
`65d07f3bd1140a75fa9122ef9ec31f739c4a744a117752b38f576b2493457fef`.
The exact-set comparison reports only `_object_get_bounding_sphere` as newly
exact, preserves all six inherited exact owners, and reports no loss.
## One-shot discipline and validation
`git diff --check` and the production compiler's no-output `/Zs` check passed
before emission. Exactly one normal selected-object compiler edge produced the
retained artifact. No assertion spelling, macro, field, declaration, include
topology, flag, or compiler retry occurred.
The complete graph and campaign gates pass:
- 377/833 complete objects overall and 275/468 for Halo;
- 511,119/2,198,102 meaningful code bytes and 4,240/11,060 functions overall;
- 498,205/1,770,166 Halo code bytes and 4,073/7,574 Halo functions;
- data unchanged at 1,836,762/4,176,062 overall;
- semantic audit: 470 units, 4,352 functions evaluated, 4,206 semantic exact,
4,266 accepted exact, and zero unit errors;
- admission: zero candidates, contradictions, or revocations;
- parks: three active, zero stale, zero invalid;
- tooling tests: 179/179 pass, with only the inherited pytest cache-permission
warning.
No protected source, configuration entry, completion label, push, amend, or
history rewrite is involved.
@@ -1,112 +0,0 @@
# Action-vehicle 2D-distance recovery (2026-08-27)
## Result
The first and only ordinary code-producing candidate makes `_distance2d`
strict exact in `source/ai/action_vehicle.obj`. The object advances from 4/17
to 5/17 exact functions and from 95/4,909 to 132/4,909 meaningful code bytes.
It remains `NonMatching`; no data, completion-label, semantic-exception, or
parked-function credit is claimed.
The four inherited exact owners, `_action_vehicle_update`,
`_action_vehicle_flush_structure_indices`, `_point_from_line2d`, and
`_negate_vector2d`, remain exact.
## Frozen target and provenance evidence
January defines an external cdecl `real(real_point2d const *, real_point2d
const *)` owner with 37 meaningful and 48 padded bytes. It subtracts the two
point components, squares and adds them on the x87 stack, applies `fsqrt`, and
returns the result. The function has no relocation.
The canonical inline implementation in `source/math/real_math.h` supplies the
same signature and computes the magnitude of the vector from `a` to `b`. The
HCEA later-build source at commit
`570c83fd9c365dad6f2a3e7041705d5b84c7847c` independently spells the same two
component differences and square root. Its source blob is
`3757f43b014e35fe4945b9b678c7857cec58613b`. HCEA supplies semantic and type
provenance only; January PC COFF remains the sole padded-byte authority.
The complete Claude campaign tree, Stian action-vehicle sources and
verification metadata, all current ledgers, and all repository revisions were
searched before emission. None contains an earlier action-vehicle definition,
candidate artifact, or fixed-point record for this owner. The immediately
preceding point-from-line ledger explicitly excluded it to preserve the present
independent one-shot boundary.
`_distance3d` and `_distance_squared2d` remained outside this wave. Their
different x87 stack shapes require separate frozen evidence and production
shots.
## Retained typed source and house-rule audit
The existing `actions.h` include brings in the canonical inline math header.
A translation-unit-local macro rename retains the inline spelling privately
while allowing January's external owner to be defined from the canonical
body:
```c
#define distance2d distance2d_inline
#include "actions.h"
#undef distance2d
real distance2d(
real_point2d const *a,
real_point2d const *b)
{
real_vector2d v;
return magnitude2d(vector_from_points2d(a, b, &v));
}
```
Every parameter is on its own line, the C89 local declaration precedes the
statement, typed helpers and named fields provide the arithmetic, and the
function ends with an explicit value return. It contains no assembly,
`register`, `volatile`, optimizer barrier, pragma, intrinsic, attribute, raw
address, byte-offset access, pointer/integer reconstruction, pointer or union
pun, undefined behavior, synthetic anchor, object patch, comparator exception,
or byte forcing.
## Immutable artifacts and strict comparison
- Base commit: `608181dec91c7d657c503d864c115354ed9946a7`.
- Pre-wave source blob: `d6af877431ebe5ae8d02c961bb091aa2f6d17683`.
- Frozen retained source blob: `8b35c87381770b48b7a6118555b6a60c61b54757`.
- January split object SHA-256:
`49F00A0115CECF578E51102FC33F665F876A4FAFCB78EE3B53520545406EF769`.
- Baseline candidate object SHA-256:
`4A2BBD6AFD7DB99DC2636DE3AD3F590B0B52A3D969D450347FB3F0B366BC75F1`.
- First-shot object SHA-256:
`5901C77965196428278E8290250BAB1B493581466B74A2FF60D5CF70CDEE0285`.
- Compiler: Microsoft XDK 3911 CL 13.00.9254.1 with the unchanged natural
`/O2 /Oy- /DDEBUG /Dxbox` flags and include graph.
The hardened comparator proves all 48 padded bytes and zero-relocation
identity exact. Target and candidate share normalized SHA-256
`3c78450b16a81d94946b346e70e6b6c0b6d43ce353c74c1a18cd64d8e37eff9d`.
The exact-set comparison reports only `_distance2d` as newly exact, preserves
all four inherited exact owners, and reports no loss.
## One-shot discipline and validation
`git diff --check` and the production compiler's no-output `/Zs` check passed
before emission. Exactly one normal selected-object compiler edge produced the
retained artifact. No expression, helper spelling, declaration, include
topology, flag, or compiler retry occurred.
The complete graph and campaign gates pass:
- 377/833 complete objects overall and 275/468 for Halo;
- 510,957/2,198,102 meaningful code bytes and 4,238/11,060 functions overall;
- 498,043/1,770,166 Halo code bytes and 4,071/7,574 Halo functions;
- data unchanged at 1,836,762/4,176,062 overall;
- semantic audit: 470 units, 4,350 functions evaluated, 4,204 semantic exact,
4,264 accepted exact, and zero unit errors;
- admission: zero candidates, contradictions, or revocations;
- parks: three active, zero stale, zero invalid;
- tooling tests: 179/179 pass, with only the inherited pytest cache-permission
warning.
No protected source, configuration entry, completion label, push, amend, or
history rewrite is involved.
@@ -1,138 +0,0 @@
# Action-vehicle 3D-distance fixed point (2026-08-27)
## Result
Two separately frozen, evidence-backed waves for `_distance3d` were cleanly
rejected. Both candidates preserve all five inherited exact functions in
`source/ai/action_vehicle.obj`, match January's 64-byte padded extent, and
contain zero relocations, but neither matches the target's x87 instruction
schedule. All candidate source was removed and the exact pre-wave source blob
was restored.
The object therefore remains at 5/17 exact functions and 132/4,909 meaningful
code bytes. No function, data, completion-label, semantic-exception, or
parked-function credit is claimed.
## Frozen target and provenance evidence
January defines an external cdecl `real(real_point3d const *, real_point3d
const *)` owner with 51 meaningful and 64 padded bytes. It loads the three
component differences, squares `x`, then `y`, accumulates those two, squares
and accumulates `z`, exchanges the result into the expected x87 slot, applies
`fsqrt`, drops the two residual stack values, and returns. It has no
relocation. Its normalized SHA-256 is
`4cdb6852c1705b5f4af050b829c52d1342a122dccb39f2291f477c6829908b84`.
The canonical inline implementation in `source/math/real_math.h` supplies the
fixed signature and nested vector/magnitude semantics. The independent HCEA
later-build source at commit `570c83fd9c365dad6f2a3e7041705d5b84c7847c`
spells three scalar differences followed by the left-associated sum of their
squares. Its source blob is
`0918df559cfdfb35e458f74fc6ebd1592f9eda97`. HCEA is semantic and source-shape
provenance only; January PC COFF remains the sole padded-byte authority.
The complete Claude campaign tree, Stian action-vehicle source and verification
metadata, every current ledger, and all repository revisions were searched.
None provides another January-authenticated source shape or a prior attempt for
this owner.
## Wave one: canonical nested helper
The first frozen wave used the canonical inline body under a source-local macro
rename:
```c
real distance3d(
real_point3d const *a,
real_point3d const *b)
{
real_vector3d v;
return magnitude3d(vector_from_points3d(a, b, &v));
}
```
It emits the same loads, three differences, 64-byte padded extent, and zero
relocations, but squares `z`, then `x`, then `y`. Its normalized SHA-256 is
`01d7809ae796fc6e9a4a6c2ca73f1e156770bdcff33dd17fc212c50d7279a841`.
The immutable candidate object SHA-256 is
`995D0A95996A30F24DFA5D09CD6CDE66D1AC8429AD61F14F1B0C93929C95003C`.
The emitted x87 order and the independent HCEA scalar spelling constituted
genuine new evidence, so the canonical source was restored before opening a
separate second wave.
## Wave two: donor-derived scalar topology
The second frozen wave used the HCEA scalar topology while retaining current
typed names and the canonical square-root helper:
```c
real distance3d(
real_point3d const *a,
real_point3d const *b)
{
real dx = b->x - a->x;
real dy = b->y - a->y;
real dz = b->z - a->z;
return square_root(dx*dx + dy*dy + dz*dz);
}
```
It also emits the same loads, differences, extent, and zero-relocation shape,
but squares `z`, then `y`, then `x`. Its normalized SHA-256 is
`21014f2a01d8cc834186684d7f719a227bf489bc06c34d4890f9b4a862f356f5`.
The immutable candidate object SHA-256 is
`245CE326C975C10738CA9B8E74CF7ECD07E7A6C419C31DA7029C8A667FA55B61`.
No further retry is justified by current evidence. Reopen this owner only with
a new January-era source donor, compiler intermediate evidence, or a lawful
source topology independently explaining the target's exact x87 ownership and
drop order.
## Immutable baseline and restoration
- Base commit: `6076c6b4953ded466b8f280fc5177999de537d2e`.
- Pre-wave and restored source blob:
`8b35c87381770b48b7a6118555b6a60c61b54757`.
- Canonical-wave source blob:
`3dba79474fb4e09b49778ac577f78c27e08654b6`.
- Scalar-wave source blob:
`1ce486341aed567b5b8b77198d74156e28bf5387`.
- January split object SHA-256:
`49F00A0115CECF578E51102FC33F665F876A4FAFCB78EE3B53520545406EF769`.
- Frozen baseline candidate object SHA-256:
`5901C77965196428278E8290250BAB1B493581466B74A2FF60D5CF70CDEE0285`.
- Compiler: Microsoft XDK 3911 CL 13.00.9254.1 with the unchanged natural
`/O2 /Oy- /DDEBUG /Dxbox` flags and include graph.
Each wave passed `git diff --check` and a no-output `/Zs` parse before its one
normal code-producing compile. Neither used assembly, `register`, `volatile`,
optimizer barriers, pragmas, intrinsics, attributes, raw addresses,
pointer/integer reconstruction, pointer or union puns, undefined behavior,
synthetic anchors, object patches, comparator exceptions, or byte forcing.
After restoration, the complete exact-set comparison again contains exactly
`_action_vehicle_update`, `_action_vehicle_flush_structure_indices`,
`_point_from_line2d`, `_distance2d`, and `_negate_vector2d`, with no lost or
new owner.
## Validation
The restored complete graph and campaign gates pass at the unchanged campaign
totals:
- 377/833 complete objects overall and 275/468 for Halo;
- 510,957/2,198,102 meaningful code bytes and 4,238/11,060 functions overall;
- 498,043/1,770,166 Halo code bytes and 4,071/7,574 Halo functions;
- data at 1,836,762/4,176,062 overall;
- semantic audit: 470 units, 4,350 functions evaluated, 4,204 semantic exact,
4,264 accepted exact, and zero unit errors;
- admission: zero candidates, contradictions, or revocations;
- parks: three active, zero stale, zero invalid;
- tooling tests: 179/179 pass, with only the inherited pytest cache-permission
warning.
No protected source, configuration entry, completion label, push, amend, or
history rewrite is involved.
@@ -1,109 +0,0 @@
# Action-vehicle squared 2D-distance recovery (2026-08-27)
## Result
The first and only ordinary code-producing candidate makes
`_distance_squared2d` strict exact in `source/ai/action_vehicle.obj`. The
object advances from 5/17 to 6/17 exact functions and from 132/4,909 to
165/4,909 meaningful code bytes. It remains `NonMatching`; no data,
completion-label, semantic-exception, or parked-function credit is claimed.
All five inherited exact owners remain exact: `_action_vehicle_update`,
`_action_vehicle_flush_structure_indices`, `_point_from_line2d`,
`_distance2d`, and `_negate_vector2d`.
## Frozen target and provenance evidence
January defines an external cdecl `real(real_point2d const *, real_point2d
const *)` owner with 33 meaningful and 48 padded bytes. It loads the two
component differences, squares `x`, squares `y`, accumulates them, drops the
residual x87 value, and returns. The function has no relocation. Its byte body
is exactly the non-square-root prefix of the already accepted adjacent
`_distance2d` owner.
The canonical inline implementation in `source/math/real_math.h` supplies the
same signature and computes the squared magnitude of the vector from `a` to
`b`. The HCEA later-build source at commit
`570c83fd9c365dad6f2a3e7041705d5b84c7847c` independently spells the two
component differences and their sum of squares. Its source blob is
`c99e46c512b4f42215c6bf887b00b63d51084011`. HCEA supplies semantic and type
provenance only; January PC COFF remains the sole padded-byte authority.
The complete Claude campaign tree, Stian action-vehicle source and verification
metadata, all current ledgers, and all repository revisions were searched
before emission. None contains an earlier action-vehicle definition,
candidate artifact, or fixed-point record for this external owner. Mentions in
other object ledgers concern independent translation-unit copies and confer no
ownership here.
## Retained typed source and house-rule audit
The existing `actions.h` include brings in the canonical inline math header.
A translation-unit-local macro rename retains that inline spelling privately
while defining January's external owner from the canonical body:
```c
#define distance_squared2d distance_squared2d_inline
#include "actions.h"
#undef distance_squared2d
real distance_squared2d(
real_point2d const *a,
real_point2d const *b)
{
real_vector2d v;
return magnitude_squared2d(vector_from_points2d(a, b, &v));
}
```
Every parameter is on its own line, the C89 local declaration precedes the
statement, typed helpers and named fields carry the arithmetic, and the
function ends with an explicit value return. It contains no assembly,
`register`, `volatile`, optimizer barrier, pragma, intrinsic, attribute, raw
address, byte-offset access, pointer/integer reconstruction, pointer or union
pun, undefined behavior, synthetic anchor, object patch, comparator exception,
or byte forcing.
## Immutable artifacts and strict comparison
- Base commit: `9b60e35bab6620162e3e985960a693e9d48d7dce`.
- Pre-wave source blob: `8b35c87381770b48b7a6118555b6a60c61b54757`.
- Frozen retained source blob: `96eb18be20f7f1aa3e40e65063e9d65f5b57650c`.
- January split object SHA-256:
`49F00A0115CECF578E51102FC33F665F876A4FAFCB78EE3B53520545406EF769`.
- Baseline candidate object SHA-256:
`3502C791766695EDF523005BD35BD478CA266EA3930809B4D1A345C221A6DCC4`.
- First-shot object SHA-256:
`0B473CDE87C91DB8E453A108F777B1138C5556CC44B31303F509AEA8B634B048`.
- Compiler: Microsoft XDK 3911 CL 13.00.9254.1 with the unchanged natural
`/O2 /Oy- /DDEBUG /Dxbox` flags and include graph.
The hardened comparator proves all 48 padded bytes and zero-relocation
identity exact. Target and candidate share normalized SHA-256
`e12f5936181ec0b4f78951a003a60fa0a613f8f5345003e293f2b3a4a180c4f2`.
The exact-set comparison reports only `_distance_squared2d` as newly exact,
preserves all five inherited exact owners, and reports no loss.
## One-shot discipline and validation
`git diff --check` and the production compiler's no-output `/Zs` check passed
before emission. Exactly one normal selected-object compiler edge produced the
retained artifact. No expression, helper spelling, declaration, include
topology, flag, or compiler retry occurred.
The complete graph and campaign gates pass:
- 377/833 complete objects overall and 275/468 for Halo;
- 510,990/2,198,102 meaningful code bytes and 4,239/11,060 functions overall;
- 498,076/1,770,166 Halo code bytes and 4,072/7,574 Halo functions;
- data unchanged at 1,836,762/4,176,062 overall;
- semantic audit: 470 units, 4,351 functions evaluated, 4,205 semantic exact,
4,265 accepted exact, and zero unit errors;
- admission: zero candidates, contradictions, or revocations;
- parks: three active, zero stale, zero invalid;
- tooling tests: 179/179 pass, with only the inherited pytest cache-permission
warning.
No protected source, configuration entry, completion label, push, amend, or
history rewrite is involved.
@@ -1,108 +0,0 @@
# Action-vehicle vector-negation recovery (2026-08-27)
## Result
The first and only ordinary code-producing candidate makes
`_negate_vector2d` strict exact in `source/ai/action_vehicle.obj`. The object
advances from 2/17 to 3/17 exact functions and from 35/4,909 to 60/4,909
meaningful code bytes. It remains `NonMatching`; no data, completion-label,
semantic-exception, or parked-function credit is claimed.
Both inherited exact owners, `_action_vehicle_update` and
`_action_vehicle_flush_structure_indices`, remain exact.
## Frozen target and provenance evidence
January defines an external cdecl `real_vector2d *(real_vector2d const *,
real_vector2d *)` owner with 25 meaningful and 32 padded bytes. It loads the
two source floats in order, negates each with x87 `fchs`, stores them to the
corresponding result fields, leaves the result pointer in `EAX`, and returns.
The function has no relocation.
The canonical inline implementation in `source/math/real_math.h` independently
supplies the exact signature and named-field semantics. The HCEA later-build
source at commit `570c83fd9c365dad6f2a3e7041705d5b84c7847c` independently performs the same
two component negations and returns the result pointer. Its source blob is
`2c138a6375cc6957f91a3af3756ff5309f12518b`. HCEA supplies semantic and type
provenance only; January PC COFF remains the sole padded-byte authority.
The complete Claude campaign tree at
`0e1fc785b7f5fb8c8d6fec8cae667005aa663957`, Stian sources and verification
metadata, all current object ledgers, and all repository revisions were
searched before emission. They contain no earlier action-vehicle definition,
candidate artifact, or fixed-point record for this owner. The broader Claude
and Stian action-vehicle reconstructions concern larger actor-action bodies and
do not alter this standalone math leaf's signature or ownership.
## Retained typed source and house-rule audit
The existing `actions.h` include brings in the canonical inline math header.
A translation-unit-local macro rename keeps that inline implementation
available as `negate_vector2d_inline` while allowing January's external owner
to be defined naturally:
```c
#define negate_vector2d negate_vector2d_inline
#include "actions.h"
#undef negate_vector2d
real_vector2d *negate_vector2d(
real_vector2d const *a,
real_vector2d *result)
{
result->i = -a->i;
result->j = -a->j;
return result;
}
```
Every parameter is on its own line, the implementation uses the named typed
fields, and the function ends with an explicit value return. It contains no
assembly, `register`, `volatile`, optimizer barrier, pragma, intrinsic,
attribute, raw address, byte-offset access, pointer/integer reconstruction,
pointer or union pun, undefined behavior, synthetic anchor, object patch,
comparator exception, or byte forcing.
## Immutable artifacts and strict comparison
- Base commit: `21ab835f9748f2423d8c6a77c0da99f2b42eebc3`.
- Pre-wave source blob: `21e056ab7bf30cb7f746e8a610ca15146a4785ae`.
- Frozen retained source blob: `18146aae0ffe9fe419384a27c204a04aa7d276f0`.
- January split object SHA-256:
`49F00A0115CECF578E51102FC33F665F876A4FAFCB78EE3B53520545406EF769`.
- Baseline candidate object SHA-256:
`E87C78FD8A92B66B61018183132ED60DD1CEE3AAB8CCF80FF471F9B8818CF276`.
- First-shot object SHA-256:
`128B7726809D6A1814973851CB71E5F5B1023F34D4A012AD297482FA1B3EDF5D`.
- Compiler: Microsoft XDK 3911 CL 13.00.9254.1 with the unchanged natural
`/O2 /Oy- /DDEBUG /Dxbox` flags and include graph.
The hardened comparator proves all 32 padded bytes and zero-relocation
identity exact. Target and candidate share normalized SHA-256
`5a29b0bda878f7b2a152c71c0056cd52ba442629e5177fb7f7617ac83bc3eed3`.
The exact-set comparison reports only `_negate_vector2d` as newly exact,
preserves both inherited exact owners, and reports no loss.
## One-shot discipline and validation
`git diff --check` and the production compiler's no-output `/Zs` check passed
before emission. Exactly one normal selected-object compiler edge produced the
retained artifact. No field spelling, statement order, include topology,
flag, or compiler retry occurred.
The complete graph and campaign gates pass:
- 377/833 complete objects overall and 275/468 for Halo;
- 510,885/2,198,102 meaningful code bytes and 4,236/11,060 functions overall;
- 497,971/1,770,166 Halo code bytes and 4,069/7,574 Halo functions;
- data unchanged at 1,836,762/4,176,062 overall;
- semantic audit: 470 units, 4,348 functions evaluated, 4,202 semantic exact,
4,262 accepted exact, and zero unit errors;
- admission: zero candidates, contradictions, or revocations;
- parks: three active, zero stale, zero invalid;
- tooling tests: 179/179 pass, with only the inherited pytest cache-permission
warning.
No protected source, configuration entry, completion label, push, amend, or
history rewrite is involved.
@@ -1,124 +0,0 @@
# `action_vehicle.obj` Jonas one-leaf wave
## Result
This bounded source-only wave starts from authoritative integration commit
`d3453bd46972f3c24148b2de14f495286fa3637c`. The three permitted HCEA-backed
action-vehicle bodies were emitted together in exactly one natural candidate
compile. `_action_vehicle_flush_structure_indices` was independently strict
and is retained. `_action_vehicle_begin` and `_action_vehicle_control` missed
and were removed immediately, without a spelling, declaration, control-flow,
or compiler-control retry. The pre-existing exact `_action_vehicle_update`
body and bytes are unchanged.
`action_vehicle.obj` advances from 1/17 to 2/17 exact functions and remains
`NonMatching`.
| Retained function | Meaningful | Padded | Relocations | Normalized SHA-256 |
| --- | ---: | ---: | ---: | --- |
| `_action_vehicle_flush_structure_indices` | 34 | 48 | 2 | `0463c71d743e4bc0945ec5b28c91d1650764463a866ff5e552074a88f4ad1130` |
| **Wave gain** | **34** | **48** | **2** | |
The hardened COFF comparator proves identical padded bytes and exact
relocation addresses, types, destinations, and addends. The object now reports
35/4,909 meaningful code bytes and two exact functions. The target's remaining
fifteen functions and all 116 non-code bytes stay outside this wave's credit.
## Provenance and signatures
- Baseline `source/ai/action_vehicle.c` blob:
`77f976dc9617cb72d35c768e2fa2b839be7d7ad5`.
- Retained source blob/committed-blob SHA-256:
`21e056ab7bf30cb7f746e8a610ca15146a4785ae` /
`ef2f07e6e1a4ede19ce03b834a6de720c9cf1eca465299f3e80e39b1c3286308`.
- January executable SHA-256:
`4cc87b45f721270392a96f1674ed2b5cd4a7bb4355faeab4531d1cf1884d9520`.
- January split-object SHA-256:
`49f00a0115cecf578e51102fc33f665f876a4fafcb78ee3b53520545406ef769`.
- Clean HCEA semantic donor commit:
`570c83fd9c365dad6f2a3e7041705d5b84c7847c`. Its exact-name
`src/blam/ai/action_vehicle_begin.c`,
`src/blam/ai/action_vehicle_flush_structure_indices.c`, and
`src/blam/ai/action_vehicle_control.c` files supply the three natural typed
behaviors. The retained donor file's blob is
`99133bb4ae11f72755ea63cc95f4fede8c9d4182`.
- January PC disassembly independently fixes the 32-bit actor datum handle,
`actor_get`/`datum_get` access path, destination-surface field store, exact
call identities, code extents, padding, and relocation ownership. January
COFF remains authoritative for acceptance.
- Compiler: Microsoft 32-bit C/C++ Optimizing Compiler `13.00.9254.1` for
80x86, SHA-256
`483e00c47bb08d699475a642bcff15b5b2036350b31c540e88a506baf101da11`,
with the repository's unchanged XDK 3911 command and flags.
The retained signature is:
```c
void action_vehicle_flush_structure_indices(
long actor_index);
```
Existing `actors.h` and `actions.h` own the typed actor/action layout and
`actor_get` contract. Existing `cseries.h` owns `NONE`. No shared-header or
storage repair is needed.
## Authenticated behavior and one-shot rejection record
The retained body resolves the full 32-bit actor handle through `actor_get`
and invalidates
`actor->state.action_data.vehicle.destination_surface_index` with `NONE`.
Its target relocations are `DIR32 _actor_data` at `+8` and
`REL32 _datum_get` at `+15`.
All three candidates were compiled together once. The following two misses
were removed without retry:
| Rejected function | Target meaningful / padded / relocs | Target hash | First natural candidate |
| --- | ---: | --- | --- |
| `_action_vehicle_begin` | `76 / 80 / 3` | `5e5ac406a3045fd7df7a8414f08608cd1ece5bd940ea167941fb293bd192a8f0` | `80 / 3`, SHA `fef3ce0d9fc229b139957cfc30fdcc6d1a4a212be1536f2ebc864411f2046c65`; the relocation addresses and identities are equal, but normalized instruction bytes differ. |
| `_action_vehicle_control` | `175 / 176 / 3` | `81612c85cf900b15c2de8ea6d52646f5727a96fbea5e2f13018e6f9613fcd293` | `176 / 3`, SHA `3f96cbf0cdc3a58d1013ac2d44df261acc0d3b68c37ebab639956ddfa1a046ad`; the relocation addresses and identities are equal, but normalized instruction bytes differ. |
For `_action_vehicle_begin`, the common relocations are `DIR32 _actor_data`
at `+8`, `REL32 _datum_get` at `+16`, and `REL32 _game_time_get` at `+35`.
For `_action_vehicle_control`, they are `DIR32 _actor_data` at `+4`,
`REL32 _datum_get` at `+17`, and `REL32 _actor_path_has_path` at `+89`.
Both rejected COMDATs and their candidate-only includes/constants are absent
from the final object. Reopen either body only with independently preserved
January source or a newly authenticated ordinary-C contract; do not tune from
the rejected objects.
## Policy, scope, and validation
Only `source/ai/action_vehicle.c` and this new Jonas-owned ledger are changed.
No shared header, storage definition, data section, configuration, semantic
exception, parked record, completion label, frozen-five source, pre-existing
Markdown, or Claude-owned file is edited.
The retained source is readable typed C. It contains no assembly, raw address
or byte-offset dereference, pointer/integer reconstruction, pointer or union
pun, inactive-union access, undefined overflow, volatile scheduling device,
force-inline annotation, optimizer pragma/barrier, synthetic anchor, or
object-byte forcing. The final COFF's only action-vehicle code symbols are the
pre-existing `_action_vehicle_update` and retained
`_action_vehicle_flush_structure_indices`; it adds no owned storage.
- Complete 569-edge `halobetacache_build` and `libcmt_build`: pass.
- Direct hardened comparison: both `_action_vehicle_update` and
`_action_vehicle_flush_structure_indices` pass; both rejected symbols are
absent from the final COFF symbol table.
- Semantic audit: 470 units, 4,053 functions evaluated, 3,913 semantic exact,
101 hidden exact / 61,876 hidden code bytes, 3,974 accepted exact, and zero
unit errors.
- Campaign progress: 375/833 complete objects, 3,961/11,060 exact functions,
475,672/2,198,102 meaningful code bytes, and
1,835,088/4,176,062 matched data bytes. This wave adds one function and 34
meaningful bytes with no data increase.
- Admission audit: zero candidates and zero revocations; only the inherited
`source/shell/shell_xbox` completion-label contradiction remains.
- Parked validation: three active, zero stale, and zero invalid entries.
- Complete tooling suite: 179/179 tests pass; `git diff --check` is clean.
A clean committed-state snapshot followed by deletion and forced rebuilding
of `action_vehicle.obj` is the final same-path reproducibility proof. The
post-rebuild hardened comparison remains strict for both accepted functions.
No push is performed.
@@ -1,112 +0,0 @@
# Action-vehicle point-from-line recovery (2026-08-27)
## Result
The first and only ordinary code-producing candidate makes
`_point_from_line2d` strict exact in `source/ai/action_vehicle.obj`. The object
advances from 3/17 to 4/17 exact functions and from 60/4,909 to 95/4,909
meaningful code bytes. It remains `NonMatching`; no data, completion-label,
semantic-exception, or parked-function credit is claimed.
The three inherited exact owners, `_action_vehicle_update`,
`_action_vehicle_flush_structure_indices`, and `_negate_vector2d`, remain
exact.
## Frozen target and provenance evidence
January defines an external cdecl
`real_point2d *(real_point2d const *, real_vector2d const *, real,
real_point2d *)` owner with 35 meaningful and 48 padded bytes. It computes the
two components in order as `t*v + p`, stores them to the result point, leaves
the result pointer in `EAX`, and returns. The function has no relocation.
The canonical inline implementation in `source/math/real_math.h` independently
supplies the exact signature, parameter order, named fields, arithmetic order,
and return value. Its source blob is
`39a62c3da5b6a9003dfeb420ed554d1718596541`. January PC COFF remains the sole
padded-byte authority.
The complete Claude campaign tree, HCEA and Stian action-vehicle sources and
metadata, all current ledgers, and all repository revisions were searched
before emission. None contains a prior action-vehicle definition, candidate
artifact, or fixed-point record for this owner. The existing path-structure
ledgers mention the same canonical inline helper only as a spelling option in
other translation units; they neither define nor claim this external owner.
The adjacent `_distance2d` and `_distance3d` leaves were deliberately excluded
from this wave because their nested magnitude helpers introduce separate x87
stack-scheduling evidence. This wave froze only the direct arithmetic leaf.
## Retained typed source and house-rule audit
The existing `actions.h` include brings in the canonical inline math header.
A translation-unit-local macro rename retains that inline owner under a private
name while allowing January's external function to be defined naturally:
```c
#define point_from_line2d point_from_line2d_inline
#include "actions.h"
#undef point_from_line2d
real_point2d *point_from_line2d(
real_point2d const *p,
real_vector2d const *v,
real t,
real_point2d *result)
{
result->x = t*v->i + p->x;
result->y = t*v->j + p->y;
return result;
}
```
Every parameter is on its own line, the implementation uses named typed
fields, and the function ends with an explicit value return. It contains no
assembly, `register`, `volatile`, optimizer barrier, pragma, intrinsic,
attribute, raw address, byte-offset access, pointer/integer reconstruction,
pointer or union pun, undefined behavior, synthetic anchor, object patch,
comparator exception, or byte forcing.
## Immutable artifacts and strict comparison
- Base commit: `dccc548cc21c1dfc6a3ab8e1083888794141e506`.
- Pre-wave source blob: `18146aae0ffe9fe419384a27c204a04aa7d276f0`.
- Frozen retained source blob: `d6af877431ebe5ae8d02c961bb091aa2f6d17683`.
- January split object SHA-256:
`49F00A0115CECF578E51102FC33F665F876A4FAFCB78EE3B53520545406EF769`.
- Baseline candidate object SHA-256:
`128B7726809D6A1814973851CB71E5F5B1023F34D4A012AD297482FA1B3EDF5D`.
- First-shot object SHA-256:
`4A2BBD6AFD7DB99DC2636DE3AD3F590B0B52A3D969D450347FB3F0B366BC75F1`.
- Compiler: Microsoft XDK 3911 CL 13.00.9254.1 with the unchanged natural
`/O2 /Oy- /DDEBUG /Dxbox` flags and include graph.
The hardened comparator proves all 48 padded bytes and zero-relocation
identity exact. Target and candidate share normalized SHA-256
`d90e91bc9340636a11d9fe06c53bd87fad47428860899fd41c08c65be2cbe273`.
The exact-set comparison reports only `_point_from_line2d` as newly exact,
preserves all three inherited exact owners, and reports no loss.
## One-shot discipline and validation
`git diff --check` and the production compiler's no-output `/Zs` check passed
before emission. Exactly one normal selected-object compiler edge produced the
retained artifact. No expression, statement order, include topology, flag, or
compiler retry occurred.
The complete graph and campaign gates pass:
- 377/833 complete objects overall and 275/468 for Halo;
- 510,920/2,198,102 meaningful code bytes and 4,237/11,060 functions overall;
- 498,006/1,770,166 Halo code bytes and 4,070/7,574 Halo functions;
- data unchanged at 1,836,762/4,176,062 overall;
- semantic audit: 470 units, 4,349 functions evaluated, 4,203 semantic exact,
4,263 accepted exact, and zero unit errors;
- admission: zero candidates, contradictions, or revocations;
- parks: three active, zero stale, zero invalid;
- tooling tests: 179/179 pass, with only the inherited pytest cache-permission
warning.
No protected source, configuration entry, completion label, push, amend, or
history rewrite is involved.
@@ -1,131 +0,0 @@
# `action_vehicle.obj`: opus5 100K consolidated lane (2026-09-14)
## 1. Scope and provenance
- Source TU: `source/ai/action_vehicle.c`; target object: `build/split/source/ai/action_vehicle.obj`
(January split, SHA-256 `49F00A01...F769` per earlier ledgers).
- Compiler: Microsoft XDK 3911 CL 13.00.9254 with the repository's unchanged flags
(measured only through `tools/campaign/gate.py`; no ninja/configure run by this worker).
- Lane: `opus/100k-consolidated-20260914`, HEAD `b8cd50113`; worker claim `waveA:action_vehicle`.
- Evidence used: January COFF (code, relocations, literals); January map atlas
`scratch/atlas/action_vehicle.obj.txt`; HCEA `action_vehicle_{desirable,find_destination,setup_specific,setup_impromptu}.c`
for semantics only. Prior ledgers read: `action_vehicle_obj_evaluate_seat_symbol_reconciliation_20260913`,
`..._fable_control_intake_20260906`, `..._fable_control_reconciliation_20260906`,
`..._jonas_distance3d_fixed_point_20260827`, `..._jonas_one_leaf_20260821` and the grep hits in the
units/path_obstacles/fable intake ledgers. `branch_sweep.py source/ai/action_vehicle`: no blob with more exact rows.
- Per-function worker report (full measurements): `scratch/workers/action_vehicle.md`.
## 2. Validated baseline
Gate at lane HEAD: `== exact 10 residual 7 unwritten 0 (of 17 listed)`; no parked entries.
| Residual | Target padded / relocs / nsha16 | Base padded / relocs | First divergence |
| --- | --- | --- | --- |
| `_action_vehicle_desirable` | 272 / 7 / e3b1ecf64be4fb82 | 288 / 7 | callee-saved push `edi` vs `esi` (private ABI), dead-test layout |
| `_action_vehicle_evaluate_seat` | 976 / 30 / 9f67690555e799a6 | 960 / 31 | `mov eax,[ebp+8]` before actor load; `_vehicle_specific_definition_get` call vs `_tag_get` |
| `_action_vehicle_find_destination` | 1232 / 34 / 698d66d07e83af5b | 1264 / 34 | frame `0x464` vs `0x468`; register params `ecx`/`eax` |
| `_action_vehicle_perform` | 768 / 18 / e61cabd696765a6f | 768 / 18 | actor/vehicle register swap `edi`/`esi` |
| `_action_vehicle_setup_impromptu` | 464 / 18 / 0347ef4f8993dc07 | 464 / 18 | inlined desirable square order; find_destination call ABI |
| `_action_vehicle_setup_specific` | 336 / 15 / aa10ba89a138d06b | 336 / 15 | inlined desirable dead test (branchy vs branchless) |
| `_distance3d` | 64 / 0 / 4cdb6852c1705b5f | 64 / 0 | x87 square order (exhausted, 2026-08-27) |
## 3. Accepted controls (retained in production)
`_action_vehicle_desirable` is strict EXACT (272 padded, 272 meaningful, 7 relocations) in the whole-TU
real-file gate `== exact 11 residual 6 unwritten 0`. Two source changes, both in `action_vehicle.c`:
1. The TU-local `#define REAL_MATH_EXTERNAL_MAGNITUDE_SQUARED3D` / `#undef` pair was removed, which restores
the header's default inline linkage. January inlines `magnitude_squared3d` inside the distance test and calls it
out of line for the velocity test. With the header default, VC7 makes the same per-call choice by itself.
2. The body now reads `boolean desirable = FALSE;` followed by `if (dead) desirable = FALSE; else if (scripted)
desirable = TRUE; else { ...; if (already_inside || distance_squared3d(&body, &origin) < max*max) {...} }`.
Binary support for each part:
- January computes the three coordinate differences *after* the `already_inside` test, with no `fstp` cleanup.
So the distance is an inline helper in the `||` right operand, not a set of pre-computed locals.
- `xor bl,bl` comes before the dead test, which is the initializer.
- The not-dead body is placed after the return, which is the if/else-if chain.
- January's `setup_specific` inlines desirable with scripted=TRUE as branchless `shr cl,2; not cl; and cl,1`.
VC7 only if-converts an if/else whose arms both store constants.
- January's `setup_impromptu` inlines desirable, and its dead path jumps to the `xor bl,bl` block that it
cross-jumps with the velocity FALSE store. Without an explicit dead-arm store, the dead path jumps straight to the
upright test (measured variant s4). So the dead-arm assignment is January's, even though it restates the initializer.
This match makes no header, config or prototype change. The owner census shows one extra header-inline select-any
COMDAT, `_magnitude_squared3d`: January references it undefined, and the same COMDAT is already emitted by many
build/base AI objects. There is no data, BSS or COMMON change. The fake scan reports 0 leads.
## 4. Experiment matrix (all via `gate.py --source`; siblings stayed at 10 exact unless noted)
| Function | Shape | Size / relocs | Result |
| --- | --- | --- | --- |
| desirable | v0 named dx/dy/dz (base) | 288 / 7 | residual |
| desirable | s3 `distance_squared3d` with external magnitude define | 240 / 8 | out-of-line distance_squared3d |
| desirable | s4 s3 without the define | 272 / 7 sha | layout/ABI still off |
| desirable | va chain, no init, else-FALSE on distance fail | 272 / 7 sha | reverted |
| desirable | **vd** init FALSE + dead-arm FALSE + else-if chain | 272 / 7 | **EXACT, landed** |
| desirable | vh va + init | 272 / 7 sha | reverted |
| desirable | d1 `magnitude_squared3d(vector_from_points3d())` | sha | desirable lost exact, reverted |
| find_destination | f1 MAX / early marker vector / 3D offset cast / chosen.z | 1248 / 34, frame 0x464 | not landed |
| find_destination | f2 f1 + `0.35f*0.35f` + bsp local first | 1248 / 34 | not landed |
| find_destination | f3 helper args without POINT_FROM_LINE3D define (diagnostic) | guard FAIL (`_point_from_line3d` emitted) | discarded |
| find_destination | f4 f2 + scalarised 3D helper vectors | 1232 / 34 sha, frame 0x460, ABI unchanged | not landed |
| find_destination | f5 destination operand order | no change | discarded |
| evaluate_seat | e0 `#include "units/vehicle_definitions.h"` only | 960 / 31 (reloc name fixed) | not landed alone (no row change) |
| evaluate_seat | e1 2D helpers on 3D storage (casts, diagnostic) | 976 / 32 | discarded |
| evaluate_seat | e2 e1 + single-exit nesting | 976 / 32 | discarded |
| evaluate_seat | e3 e2 + explicit prop-loop distance | 992 / 31 | only site-2 `normalize2d` inlined vs January call |
| evaluate_seat | e4/e5/e6/e8/e3ord variants of e3 | 992 / 31 | site-2 still inline |
## 5. Do-not-repeat list
- **Measurement trap:** a `sed 's/...\r$//'` on this LF file silently kept the magnitude define, and about 25
"inline budget" probes were built against the wrong base. The real per-call inline boundary in desirable was the
TU-local define alone. Always `grep -c` the define after generating a variant base.
- desirable: named-local distance forms, `magnitude_squared3d(vector_from_points3d())`, the explicit else-FALSE
chain, and chain + init + else-FALSE are all measured non-exact.
- find_destination: the `_point_from_line3d` inline/out-of-line split cannot be reproduced without emitting the
forbidden COMDAT. Destination operand order and bsp-in-arguments are measured inert.
- evaluate_seat: none of these changes the site-2 `normalize2d` inline choice: explicit to_seat vector,
vector_from_points3d facing, assigned result, conditional context, January function order.
- `_distance3d`: see the 2026-08-27 ledger (two waves exhausted).
## 6. Residual classification
- **find_destination (policy + ABI).** Measured facts:
- January passes `ecx=actor_index, eax=vehicle_index`.
- The actor pointer is homed and all body accesses go through one derived pointer.
- The destination uses the inline `point_from_line3d` body order, while the refine offset calls `_point_from_line3d`.
- The offset vector is a real_vector3d used as perpendicular2d/normalize2d storage.
- The approach update is `MAX`, the literal is `0.35f*0.35f`, and the bsp fetch comes first.
Inferred: the register ABI follows from the body shape, which needs the forbidden COMDAT and a representation cast.
- **evaluate_seat (policy + one inline decision).** Measured facts:
- The implicit `vehicle_specific_definition_get` call is a missing include.
- The body is single-exit.
- The facing is a 3D vector normalized as 2D.
- The distance is `MIN(distance2d, distance2d)`, and January owns the 2D helper COMDATs.
Unresolved: VC7 inlines the second `normalize2d` in our build, while January calls it.
- **setup_specific, setup_impromptu, perform (ABI/register).** They are blocked by find_destination's private ABI.
setup_impromptu also differs in one x87 square order in its inlined desirable copy.
- **_distance3d.** x87 scheduling, exhausted.
## 7. Reopen criteria
- find_destination: an owner ruling that allows a select-any `_point_from_line3d` COMDAT here, or a C spelling that
produces the out-of-line offset call without it; plus admission of the offset-vector storage cast.
Start from `scratch/workers/action_vehicle/f/f4.c`.
- evaluate_seat: owner admission of the 2D-helper-on-3D-storage casts, plus evidence for the site-2 `normalize2d`
inline choice. Start from `scratch/workers/action_vehicle/e/e3.c`, with the `vehicle_definitions.h` include.
- setup_specific, setup_impromptu, perform: re-gate as soon as find_destination matches January's ABI.
- `_distance3d`: the 2026-08-27 criteria still apply.
## 8. Disposition
`action_vehicle.obj` is now 11/17 strict exact (+272 padded bytes: `_action_vehicle_desirable`). It is still active
and not Matching. No function is parked or fuzzy-landed by this lane.
## Orchestrator admission note (checkpoint 1)
`_action_vehicle_desirable` was **not admitted**. The adversarial verifier rejected the worker's strict-exact form because its dead-vehicle arm re-stores `desirable = FALSE` after the identical initializer, which is a redundant store. The orchestrator also measured the single-assignment form (no initializer, explicit `else desirable = FALSE`): 10 exact / 7 residual, so `desirable` is not exact. `action_vehicle.c` was reverted to HEAD. The `_point_from_line3d` emission and 2D representation-cast proposals are declined under the lane rules.
@@ -1,201 +0,0 @@
# `action_vehicle.obj`: opus5 150K house-clean lane, wave w1 (2026-09-14)
## 1. Scope and provenance
- Source TU: `source/ai/action_vehicle.c`. Target: `build/split/source/ai/action_vehicle.obj` (January split).
- Lane `opus/150k-house-clean-20260914`, base `12f7375d4`. Only `source/ai/action_vehicle.c` was modified.
No header, config, build, or tool file was touched. No ninja, configure, pytest, or git write was run.
- Measurement: `tools/campaign/gate.py`, `alndiff`, `relocdiff`, `tinfo`, `scratch/parkcheck.py`, and `tools/fake_match_scan.py`.
A `/Zs /W3` warning census was also run with the unit's own cflags.
- Ledgers read: all 11 `action_vehicle_obj_*` logs, including `opus5_100k_20260914` and its admission note, plus the
`opus5_100k_consolidated_HANDOFF_20260914` rejections. The 100K worker report is at
`C:\halo-worktrees\opus5-100k-consolidated-20260914\scratch\workers\action_vehicle.md`.
- `git log --all` shows 14 commits for the file. `branch_sweep`: the best historical blob has 9 exact functions.
- Evidence sources:
- January COFF: code, relocations, and COMDAT set.
- HCEA `action_vehicle_{desirable,evaluate_seat,find_destination}.c`: semantics and topology only.
- January `actors.obj` `.rdata` assert literals (section 5).
- Per-function worker report: `scratch/workers/action_vehicle.md`. Candidates are in `scratch/workers/action_vehicle/`.
## 2. Baseline (real file at lane base, guard on)
`== exact 10 residual 7 unwritten 0 (of 17 listed)`. The unit has no parks.
| Residual | Target padded / relocs | Base tags |
| --- | --- | --- |
| `_action_vehicle_desirable` | 272 / 7 | size 288 |
| `_action_vehicle_evaluate_seat` | 976 / 30 | size 960, relocs 31 |
| `_action_vehicle_find_destination` | 1232 / 34 | size 1264 |
| `_action_vehicle_perform` | 768 / 18 | sha |
| `_action_vehicle_setup_impromptu` | 464 / 18 | sha |
| `_action_vehicle_setup_specific` | 336 / 15 | sha |
| `_distance3d` | 64 / 0 | sha |
## 3. Landed (final real-file gate `== exact 12 residual 5`, guard passed)
| Function | Padded | Meaningful | Relocs | Normalized sha16 |
| --- | ---: | ---: | ---: | --- |
| `_action_vehicle_perform` | 768 | 767 | 18 | e61cabd696765a6f |
| `_action_vehicle_setup_specific` | 336 | 328 | 15 | aa10ba89a138d06b |
The gain is **1,104 padded / 1,095 meaningful** code bytes. All 10 baseline-exact rows are unchanged. The row diff is:
- perform and setup_specific: residual -> EXACT;
- find_destination: tag 1264 -> 1248.
### 3.1 New lever: callee register pressure through a late-bound position pointer (find_destination)
January `action_vehicle_find_destination` is a private register-ABI function. Every caller loads
`ecx = actor_index` and `eax = vehicle_index`, then pops 0x18.
**How January's body uses the actor pointer.** The actor datum pointer is stored once at entry, to `[ebp-0x14]`.
It is loaded exactly once, at +0x113, immediately after the anchor-point selection, into `edi`:
mov edi,[ebp-0x14]; fsub [edi+0x12c]; add edi,0x12c; ...
All later body-position reads go through that one pointer, including the `debug->field_C8` struct copy
(`mov edx,edi`). In the base source the actor pointer was used at every site, so it took `ebx`, and
`vehicle_index` lost its register parameter.
**Source change.** `real_point3d const *actor_position;` is declared with the block locals. It is assigned
`&actor->input.position.body_position` immediately before its first use, which is the entry-direction computation after
the anchor selection. Every body-position read goes through it.
**Why the assignment point matters (measured):**
| Placement of the pointer | Result |
| --- | --- |
| Initialized at the top of the if-block (f2) | ABI unchanged |
| Initialized at the top of the function (f5) | ABI unchanged |
| Assigned just before the anchor selection (f8) | ABI unchanged |
| Assigned just after the anchor selection (f3 / landed m1) | January ABI |
| Declared and initialized in a nested block after the anchor selection (f4) | January ABI (not used, because it needs a bare block) |
With the pointer assigned after the anchor selection, the pointer's live range begins where January's `edi` derivation begins.
Only the minimal routing change was landed. The residual rule does not allow more changes to this function.
find_destination stays residual at 1248/34 with frame 0x468 (January 0x464). Its first 0x55 bytes are now identical to January.
### 3.2 Non-redundant desirable chain (required prerequisite)
The landed desirable has no initializer. It reads:
if (dead) desirable = FALSE;
else if (scripted) desirable = TRUE;
else { ...; desirable = FALSE; if (already_inside || distance_squared3d(body, origin) < max*max) { desirable = TRUE; if (moving) desirable = FALSE; } }
The upright veto follows unchanged. The TU-local `REAL_MATH_EXTERNAL_MAGNITUDE_SQUARED3D` define/undef pair is removed, as the 100K lane
proved necessary. Semantics are identical to the base on all inputs, and no store restates an earlier value. This avoids the
redundant dead-arm re-store that got the 100K `vd` form rejected.
This body gives desirable January's private ABI (`eax = actor_index`, `esi = vehicle_index`) and January's branch layout.
- **perform:** the new ABI removes perform's actor/vehicle register swap.
- **setup_specific:** its inline copy gets January's branchless dead/scripted if-conversion.
Measured prerequisites:
| Variant | Result |
| --- | --- |
| Base desirable + m1 | perform, setup_specific residual |
| d1 keeping the magnitude define | perform grows to 880 bytes |
| d3 (d1 with named dx/dy/dz) | setup_specific exact, perform not |
| d2 (boolean-expression form) | desirable no longer inlined into setup_impromptu |
desirable itself stays residual at 288/7. The only remaining difference is that the not-scripted `desirable = FALSE` store is not hoisted
into January's entry `xor bl,bl`.
### 3.3 Checks
- Point guard passed.
- parkcheck: no parks.
- Owner census against `build/base`: one added select-any COMDAT, `_magnitude_squared3d`. January references it undefined from desirable
and setup_impromptu; it is the systemic header-inline class. There is no data, BSS, or COMMON change, and nothing is dropped.
- Fake scan: 0 leads (base 0).
- `/W3` warnings: identical to the base.
## 4. Not landed
| Function | Final | Classification / reason |
| --- | --- | --- |
| `_action_vehicle_evaluate_seat` | 960/31 in the real file; **strict EXACT scratch candidate** `E1.c` | Blocked only by the earlier orchestrator decline of 2D-helper casts; see section 5 |
| `_action_vehicle_find_destination` | 1248/34 | Offset-vector representation cast, plus x87 operand-order ties (below) |
| `_action_vehicle_desirable` | 288/7 | Owner ruling on the vd dead-arm restatement |
| `_action_vehicle_setup_impromptu` | 464/18 sha | Depends on vd. With vd + m1 only the inlined x,z,y square order differs |
| `_distance3d` | 64 sha | Exhausted per the 2026-08-27 ledger |
**find_destination cast diagnostic (x2/x5).** Size 1232, frame 0x464, and January's success/failure epilogue layout are reached with a
single-exit `found` flag. The remaining x87 operand-order differences were measured inert:
- destination `fld down.i; fmul t`: operand swap, `test.x + down.i*t`, and the inline point_from_line3d body (diagnostic, guard failure)
all still load `t` first;
- the refine dot operand order;
- the offset_distance temp lifetime;
- the chosen-to-actor square order.
**distance3d via a genuine caller (g1).** Using the header-inline copy through `distance3d(&marker, hint_point)` emits the header body with
sha 01d7809ae796fc6e, which is the wave-one z,x,y schedule. That is a measured negative: the COMDAT body does not depend on its callers.
## 5. evaluate_seat: new levers and the proposal
The 100K lane stopped at 992/31: its cast form still inlined the second `normalize2d`. Three new measured VC7 levers close
that gap. All three are cast-independent mechanisms.
1. **Loop spelling controls post-loop inline frequency.**
- `for (prop = next; prop; prop = next)` keeps `normalize2d(&to_seat_direction)` inline.
- `while (prop = prop_iterator_next(&iterator))`, or `prop = next; while (prop) {...; prop = next;}`, makes it an out-of-line call
as in January.
- A diagnostic with the loop deleted also puts the call out of line. The rotated for-loop inflates the estimated frequency of the code
after it.
2. **Sibling scope shares a frame slot.** Putting the 8-byte `prop_iterator` in its own block around the loop lets it share `[ebp-0x18]`
with `to_seat_direction`. The frame goes from 0x5c to 0x54.
3. **Single exit (HCEA topology).** The body sets `result = TRUE` after the entrance point is found. A conflicting actor does
`result = FALSE; break;`. The tail sits in `if (result)` with one `return result;`. This reproduces January's three epilogues:
the threaded `xor al,al`, the tail-duplicated `mov al,1`, and `mov al,bl`.
The candidate `scratch/workers/action_vehicle/E1.c` (the current real file plus `E1.txt`) makes evaluate_seat **strict EXACT at 976/30/9f67690555e799a6**.
- Whole-TU gate: exact 13, with all 12 real-file exact rows kept.
- Point guard passed; fake scan 0 leads.
- Census: adds a header-inline `_dot_product2d` COMDAT (systemic class).
- It also adds the legal `#include "units/vehicle_definitions.h"`. This removes the base's C4013 implicit
`vehicle_specific_definition_get` call to a nonexistent `_vehicle_specific_definition_get`.
**Casts in the candidate:**
- `normalize2d((real_vector2d *)&facing)`;
- `MIN(distance2d((real_point2d *)&body, (real_point2d *)&entrance_point), distance2d(... &seat_point))`;
- `vector_from_points2d((real_point2d *)&body, (real_point2d *)&seat_point, &to_seat_direction)`;
- `dot_product2d(&to_seat_direction, (real_vector2d *)&actor->input.facing_vector)`.
**New January-side evidence for these casts:**
- January `actors.obj` `.rdata` contains the assert literals `(real_vector2d *) &actor->input.facing_vector` and
`(real_vector2d *) &control_data.facing_vector`. These are stringified by `assert_valid_real_normal2d`, and canonical
`source/ai/actors.c:1946-1950` and `4167-4171` already reproduce them. So Bungie's AI code applied 2D helpers to 3D vectors through
exactly this cast.
- The canonical tree already carries 31 such casts in admitted exact code. Examples:
- `action_obey.c:2449` `normalize2d((real_vector2d *)&facing) == 0.0f`;
- `action_obey.c:2615-2622`;
- `action_charge.c:769/834`;
- `actor_combat.c:2310`;
- `actor_firing_position.c:1548`.
- January's own action_vehicle COMDAT set is `point_from_line2d`, `distance_squared2d`, `distance2d`, `negate_vector2d`, `distance3d`,
in `real_math.h` order. It shows that this TU instantiated the 2D helpers.
**ORCHESTRATOR-PROPOSAL:** admit these January-attested casts for `action_vehicle_evaluate_seat`, and apply `E1.txt` (the function body plus the
include) to `source/ai/action_vehicle.c`. Expected gain: 976 padded bytes. No header or config change; the change has no external consumers.
## 6. Do-not-repeat (this wave)
- desirable:
- d1/d2/va/d3 and d1 with the magnitude define (all measured above);
- the dead-arm restatement (vd) is the only exact form.
- find_destination pointer placement: f2, f5 and f8 do not change the ABI. Accessor binding on the first use only (f1) does not change it either.
- The destination x87 load order is inert to operand order, sum order, and the inline point_from_line3d body.
- setup_impromptu: d2 stops the desirable inline.
- `_distance3d`: the header copy via a genuine caller gives the z,x,y body.
- evaluate_seat, still inline: nested `normalize2d(vector_from_points2d(...))` (e4), splitting the first condition into nested ifs (e5),
and a no-op cast on the site-2 argument (ed1).
## 7. Reopen criteria
- **evaluate_seat:** the cast ruling in section 5 (candidate ready).
- **find_destination:** the cast ruling, plus a source spelling that controls the destination/refine x87 operand order. Start from
`scratch/workers/action_vehicle/x5.c`. The closer cast-free body is `f7.c`.
- **desirable, setup_impromptu:** an owner ruling on vd, or a non-redundant source whose FALSE store VC7 hoists to the entry.
After that, the inlined x,z,y square order in setup_impromptu remains.
- **`_distance3d`:** the 2026-08-27 criteria.
@@ -1,166 +0,0 @@
# action_vehicle.obj - opus5 250K house-clean lane, wave w3d (2026-09-15)
Unit: `source/ai/action_vehicle.c`. Worker notes: `scratch/workers/w3d_action_vehicle.md`.
Scratch: `scratch/workers/w3d_action_vehicle/`. Predecessors: the 11 earlier
`action_vehicle_obj_*` ledgers, in particular `_obj_opus5_100k_20260914.md` and `_obj_opus5_150k_w1_20260914.md`.
## Baseline and result
| | exact | residual | unwritten |
| --- | ---: | ---: | ---: |
| HEAD (baseline, guard on) | 12 | 5 | 0 |
| after w3d | 13 | 4 | 0 |
Newly strict EXACT: `_action_vehicle_evaluate_seat` - 976 padded bytes, 30 relocations, sha 9f67690555e799a6.
No parks for this unit; no baseline-exact row changed; `_point_from_line3d` emitted-symbol guard passed.
Remaining non-exact rows: `_action_vehicle_desirable` 272 [size 288, sha], `_action_vehicle_find_destination`
1232 [size 1248, sha], `_action_vehicle_setup_impromptu` 464 [sha], `_distance3d` 64 [sha].
## 1. What unblocked the object
`scratch/w3/laws_w3.md` section B lists, under "Newly ADMITTED by verifiers (do not avoid)":
"(real_point2d const *)&point3d casts into 2D helpers (32 source sites, HCEA); A15 role order for distance2d",
landed in wave w3a as `_actor_move_avoidance_setup` (see `actor_moving_obj_opus5_150k_w3_20260914.md` line 77).
Wave w1 had already produced a strict-EXACT `_action_vehicle_evaluate_seat` body and withheld it for exactly that
policy. The 2026-09-15 owner rulings (brief sections 9b/9c) do not touch the cast class, so no new ruling was needed.
## 2. `_action_vehicle_evaluate_seat` - EXACT, landed
Target 976 / 30 / 9f67690555e799a6; HEAD was 960 / 31 / sha.
Structural census before the work:
- One relocation too many (31 vs 30). January calls `_normalize2d` twice (@0x00c1 for the entry facing, @0x0283
for the to-seat direction) and `_tag_get` twice; ours inlined the second normalize2d and emitted an extra
`_tag_get` produced by the C4013 implicit declaration of `vehicle_specific_definition_get` at line 471
(`scratch/gate4/c4013.txt`).
- 16 bytes short (960 vs 976) from the same two effects plus the frame.
The landed body combines five measured levers:
1. **A7 missing prototype.** `#include "units/vehicle_definitions.h"` - the genuine owner, which defines
`vehicle_specific_definition_get` as a macro over `tag_get` at vehicle_definitions.h:30. No consumer-local
prototype (R8). This also removes the paired C4047 "differs in levels of indirection" warning.
2. **Admitted 2D-helper casts.** `normalize2d((real_vector2d *)&facing)`,
`distance2d((real_point2d const *)&actor->input.position.body_position, (real_point2d const *)&entrance_point)`,
the MIN of that and the seat distance, `vector_from_points2d(... , &to_seat_direction)` and
`dot_product2d(&to_seat_direction, (real_vector2d const *)&actor->input.facing_vector)`. `const` is used wherever
the real_math.h parameter is `const`, matching the admitted w3a idiom.
3. **A23-class loop spelling.** `while (prop = prop_iterator_next(&iterator))`. A rotated
`for (prop = next; prop; prop = next)` inflates the estimated frequency of the code after the loop and inlines
the second `normalize2d`; the while form leaves it out of line exactly like January. The idiom is already in the
tree at actions.c:4454, action_obey.c:1826 and actor_looking.c:734.
4. **Sibling scope.** The prop iterator and `prop` live in their own block around the loop, so VC7 shares their
8-byte slot with `to_seat_direction` ([ebp-0x18]) and the frame drops 0x5c -> 0x54.
5. **A3/A29 single exit.** `result = TRUE` after the entrance-point success, `result = FALSE; break;` for the
conflicting actor, the whole publish tail under `if (result)`, one `return result;`. This reproduces January's
three epilogues: the break threads to `xor al,al`, the success path tail-duplicates `mov al,1`, the failures
share `mov al,bl`.
6. **A17 discarded validation call.** The `if (!allow_any_seat)` block is the bare statement
`vehicle_specific_definition_get(vehicle->definition_index);` followed by
`unit_seat_is_driver(vehicle_index, seat_index);`. The earlier candidate used an unused local plus a `(void)`
cast, which A17 rejects.
Shapes: E1 (the w1 body, re-gated at this HEAD) EXACT; v1 = E1 plus the const casts and the A17 bare statement,
EXACT - landed.
Checks: all 12 baseline-EXACT rows preserved (row-by-row `--all` diff); guard passed; `scratch/parkcheck.py` reports
no parks for the unit; owner census versus `build/base` adds only `_dot_product2d`, a real_math.h select-any
header-inline COMDAT of the class laws_w2 A4 accepts as systemic (`_vector_from_points3d`, `_magnitude_squared3d`,
`_normalize2d`, `_square_root`, `_scale_vector2d`, `_vector_from_points2d`, `_magnitude2d`, `_magnitude_squared2d`,
`_distance_squared3d` are already candidate-only owners at HEAD, so R14 is not newly triggered); no data, BSS or
COMMON change; `tools/fake_match_scan.py` 0 leads (HEAD 0); `CL /Zs /W3` removes C4013 and C4047 and adds nothing.
Authenticity: the routine computes the seat entry facing from entrance to seat in the XY plane, falls back to the
actor own facing vector when that degenerates, takes the nearer of the entrance and seat 2D distances, rejects the
seat when a friendly actor is already closer to the same seat, and publishes weight / range / facing / fake flags
only for an accepted seat. Every helper used is the real_math.h routine the arithmetic is named after.
## 3. `_action_vehicle_find_destination` - structural gap closed in scratch, not landed
Target 1232 / 34 / 698d66d07e83af5b; HEAD 1248 / 34, frame 0x468 against the January 0x464.
Census (recorded before any shape):
- **Relocation census by target**: 34 versus 34, every symbol count equal, with ONE real difference -
January references `__real@3dfae147` where we emit `__real@3dfae148`. `0x3dfae147` is `(float)(0.35f*0.35f)`,
the square of the float 0.35f; `0x3dfae148` is the decimal literal `0.1225f`. The HEAD source writes the folded
literal; January wrote the product. This is an emitted-constant fidelity defect, not a tie.
- **Relocation ORDER**: January calls `_global_collision_bsp_get` at @0x40f, BEFORE the `_global_up3d` read at
@0x418; ours calls it at @0x47e just before `_collision_bsp_test_vector`. By the store/call-order law the BSP
pointer is obtained before the test_point and down_vector statements, i.e. it is a block-top declaration
`struct collision_bsp *collision_bsp = global_collision_bsp_get();`, not an inline argument.
- **Frame census** (`slots.py` over `alndiff --include-equal`): January `sub esp,0x464`, ours `sub esp,0x468`.
Ours owns one extra dword `[ebp-0x50]` (3 references, all in the trailing collision block) which pushes the
collision result buffer from `[ebp-0x464]` to `[ebp-0x468]`. January packs BOTH trailing aggregates into slots
the earlier block already owns (test_point -> [ebp-0x10..-8], down_vector -> [ebp-0x24..-0x1c]); ours packs only
down_vector. laws_w2 A6 side law: nested-block locals may pack.
- **Statement order from the aligned stream**: January computes `marker.x - actor_position->x` and
`marker.y - actor_position->y` at @0x116/@0x130, i.e. in the OUTER block before `anchor_point` is assigned, and
reuses those two cells in the `t` numerator and the perpendicular sign test - a named outer-block 2D vector is
missing from HEAD. The tail is single-exit (`test al,al; je <fail>` with the success block inline, `mov al,1`
and a separate `xor al,al` epilogue). `*surface_index_reference` is stored FIRST, its load interleaved into the
x destination product.
All five defects are implemented by the w1 diagnostic body `x5.c`, which the cast admission now makes usable. On
top of it this wave spent five evidence-led shapes:
| shape | change | result |
| --- | --- | --- |
| s1 | the two hand-written 2D sign dots become `dot_product2d(...)` calls (A35(1)+(2)) | 411 vs 412 insns; also closed the point_from_line2d x87 CSE pop and the magnitude_squared3d k,j,i sum order. KEPT |
| s2 | `collision.t*down_vector.i + test_point.x` (t-first) | byte identical, inert. REVERTED |
| s3 | `scale_vector3d(global_down3d, 4.0f, &down_vector);` for the three component multiplies (A35(1) leaf age) | 412 vs 412 instructions at IDENTICAL addresses; closed the stray `fxch st(1)` and the Y/Z destination products. KEPT |
| s4 | surface_index store moved after the destination stores | worse. REVERTED |
| s5 | dot-2 argument swap | byte identical - confirms laws_w3 A44. REVERTED |
Fixed point `scratch/workers/w3d_action_vehicle/fd_cand.c` (the current real file with only find_destination
replaced): 1232 / 34, `[sha]` only - the `[size]` tag is gone - 412/412 instructions at identical addresses,
whole-TU `== exact 13 residual 4`, guard passed, fakescan 0 leads, no new warnings, owner census +`_dot_product2d`
and +`_scale_vector3d` (both A30/A4 accepted class) and the `.rdata` literal corrected to `__real@3dfae147`.
Eleven instructions remain, in four x87 leaf-age / scheduling families: the i-term of the first inlined
`dot_product2d` (@0x284), both terms of the second (@0x36e, @0x377), the interleave of the
`debug->field_F8 = chosen_point` loads against the `debug->field_F4 = TRUE` byte store (@0x3dd), and the X
destination product (@0x483/@0x48c; Y and Z now match).
NOT LANDED under brief section 6: an already-written residual may be landed only when it becomes strict EXACT, and
this candidate is not below the January size or relocation count, so the fuzzy allowance does not apply.
Reopen criterion: a source lever that flips x87 leaf age inside an inlined `dot_product2d` or a named-local product
without an R3 operand swap (A35 is diagnostic only today); or an orchestrator decision to take the strictly-closer
zero-credit body for its proven fidelity content - notably the emitted constant, where HEAD writes `0.1225f`
(`__real@3dfae148`) and January writes `0.35f*0.35f` (`__real@3dfae147`).
## 4. `_action_vehicle_desirable` - initializer proven, blocked by the pending A42 ruling
Target 272 / 7 / e3b1ecf64be4fb82; HEAD 288 / 7 (100 versus 112 instructions).
Census: relocations 7/7 with identical targets; frame slots identical apart from one extra `[ebp-8]` read. The
16-byte gap is therefore not a frame gap: it is our two extra reloads of the datum homed at `[ebp-8]` plus the
position of `xor bl,bl`.
New evidence this wave: laws_w3 A38/A42 fire. January zeroes BL at 0x2b, before the `test al,4` dead-bit compare at
0x2d, which A42 reads as proof of a declaration initializer. Shape `d4` - `boolean desirable = FALSE;` at the
declaration plus `if (!TEST_FLAG(vehicle->object.damage_flags, _object_dead_bit)) { if (scripted_request) ... else
... }`, with no redundant store anywhere - reproduces that hoisted `xor bl,bl` exactly and brings the row to
**272 bytes, the January size**, at 101 versus 100 instructions. So the initializer, not the w1 `vd` redundant
dead-arm store, is what the January entry proves.
Two families still differ in `d4`, and they are coupled:
- January keeps `vehicle_index` in its incoming ESI and the vehicle datum in EDI (pushing only ebx and edi);
`d4` moves `vehicle_index` into EDI and the vehicle datum into ESI, which flips the private ABI of the static.
`_action_vehicle_perform` (768) and `_action_vehicle_setup_specific` (336), both exact at HEAD, then fall back to
`[sha]` (`== exact 11 residual 6`). Losing an exact row is a regression, so `d4` is not landable.
- January lays the not-dead block out of line (`je 0x58`, `jmp 0x31` back); the `d4` `if (!dead) { ... }` body stays
inline. HEAD's `if (dead) X; else if ... else ...` chain reproduces the January layout, but its first arm cannot
be empty, so the only spelling carrying BOTH the initializer and the chain is the w1 `vd` redundant store.
Reopen criterion: the pending A42 owner ruling on byte-proven redundant stores (laws_w3 C3 groups
`_encounter_test_rule`, `_extract_sequence`, `_action_alert_next_position` and `_action_obey_command_begin` here).
If granted, `vd` plus this wave's initializer evidence closes desirable at 272, and the w1 ledger records that
`_action_vehicle_setup_impromptu` then has only the inlined x,z,y square order left.
## 5. Not targets this wave
`_action_vehicle_setup_impromptu` (464, [sha]) and `_distance3d` (64, [sha]) were not in the w3d TARGET list and no
shapes were spent. w1 classified setup_impromptu as desirable-dependent (it inlines desirable) plus one x87
square-order tie, and `_distance3d` as exhausted - a caller-independent header COMDAT body whose two frozen waves
and the w1 `g1` rename-removal probe all produce z,x,y or z,y,x against the January x,y,z.
-295
View File
@@ -1,295 +0,0 @@
# `actions.obj` exact-match log
## Scope and standard
This lane reconstructs `source/ai/actions.obj` from the January 14, 2002 Xbox
object. The HCEX decompilation and Fable correspondence packets are source
topology hints only. January code bytes, relocation identities, data bytes,
and symbol ownership decide every result. The lane uses the established XDK
3911 compiler and unchanged `/O2 /Oy- /DDEBUG /Dxbox` flags. Assembly,
volatile, inline-control keywords, optimizer barriers, undefined behavior,
byte forcing, flag changes, and comparator exceptions are out of scope.
## Fresh baseline (`fc8e1b07`)
The target contains 63 functions and 19,172 meaningful padded code bytes.
The fresh candidate has zero exact functions: 62 functions are absent and
`actor_action_debug_color` is 160 bytes with 15 relocations versus January's
176 bytes and 15 relocations. January also owns the 888-byte/96-relocation
`global_action_functions` constant block and the 16-byte/3-relocation
`global_actor_mode_names` data block; neither is emitted by the baseline.
Fable's exact-unique name map covers 55 of the 63 functions (17,484 January
bytes), and the local HCEX tree has readable C for those names. That coverage
does not grant match credit. It only gives this lane a much better starting
shape than decompiling every body from raw instructions.
## Strategy
The first wave is deliberately small: callback dispatchers, names, class and
default-state accessors, plus the existing debug-color function. These are
the low-risk functions that establish the action table's exact layout and
common dispatcher shapes. Header-inline helpers are not duplicated in this
translation unit; they should be emitted naturally once real callers require
them. The entire object is rebuilt and compared after every coherent wave so
an apparent local win cannot hide a sibling or ownership regression.
## Experiments
### Wave 1, natural HCEX/January topology
The six dispatcher bodies were written as ordinary callback-table lookups with
the January action-index assertion restored at its original file and line.
The two name getters and default-state getter use a single initialized result
and one range guard. `actor_action_debug_color` retained the existing body and
restored the missing signed lower-bound check. The first compile made nine of
the eleven functions exact. The only two misses were otherwise-identical
conditional branches: `jae` in the candidate versus January's `jge` in
`actor_mode_name` and `actor_action_get_default_state`.
That difference came from using `NUMBEROF(array)`, whose unsigned result
converted the short index before the upper-bound comparison. Replacing the
two unsigned counts with named enum constants kept the code readable and
restored the original signed comparison. The second compile made all eleven
functions exact. `_global_actor_mode_names` also matches as a 16-byte data
owner with the exact three string relocations and null final entry.
Strict result after Wave 1: **11/63 functions**, **1,088/19,172 code bytes**,
and **16/16 bytes of the actor-mode-name data owner** are exact. No existing
exact function regressed and no shared header was changed.
### Waves 2-4, panic and berserk leaves
The next twelve leaf functions were recovered from the exact-unique HCEX map,
then checked individually against January. The HCEX bodies supplied names and
high-level intent, while January supplied the missing Xbox-only details. Two
general source-shape findings mattered:
* The redundant sign extension in the attached-melee panic handler comes from
applying the ordinary `MAX` macro directly to a short field and an enum. An
explicitly typed temporary removes that promotion and does not match.
* January has a local-game debug bypass in the panic-from-damage threshold
which is absent from HCEX. Naming byte +8 in `ai_debug_state` recovered the
condition without changing that structure's layout. January only applies
the bypass to a local connection; spelling the connection test in the other
direction leaves one branch different.
The projectile and burning handlers also proved the responsible-unit and
active-prop call chains. January's relocation identities, not just the call
count, were used to distinguish those helpers. At the end of these waves the
object was **23/63 functions exact, 2,688/19,172 code bytes**; all earlier
matches remained exact.
### Wave 5, action stopping and transition denial
`actor_action_can_stop_guarding` first compiled to the correct logic but used
four-byte integer result materialization. Casting the two comparison
expressions to the real one-byte `boolean` type produced January's `setge al`,
`xor al,al`, and shared one-byte true exit exactly. This is a type recovery,
not a code-forcing cast: the API genuinely returns the project's byte-sized
boolean.
`actor_action_deny_transition` initially nested `encounter_get` inside
`encounter_get_squad`. That changed argument evaluation and cleanup order.
Binding the encounter datum first, as a readable named local, reproduced
January's call order and naturally emitted the 144-byte
`encounter_get_squad` header helper. Spelling the timer branch as
`combat_status >= 5` followed by expiration (with denial in the `else`) fixed
the remaining branch direction. Both functions and the helper are strict
exact.
For `actor_action_can_stop_conversing`, an initialized `TRUE` result preserves
the no-conversation exit. A compound logical expression and three explicit
early returns both placed the shared true block at the end. The readable
`if/else if/else` result chain places the first true block where January does
and makes later true branches jump backward to it. The result is strict exact,
including the scenario tag-block access and all six relocation destinations.
Strict result after Wave 5: **27/63 functions**, **3,280/19,172 code bytes**,
plus the exact actor-mode-name data owner. No exact sibling regressed.
### `actor_action_allow_cover_seeking` live residual
The semantic body is recovered and January proves that the damage comparison
uses `recent_body_damage` (+0x1c0), despite the HCEX reconstruction naming it
as shield damage. Direct definition-field accesses recover January's x87
loads and avoid a spurious float local. Four declaration/scope variants for
the short visibility duration and the long-lived `allow` boolean have been
measured. They preserve 8/8 relocation destinations but our current source
keeps the duration in `BX` and spills `allow`; January keeps `allow` in `BL`
and reuses the dead second-parameter home for the duration. Declaration order
alone did not move this allocation. The readable best body remains in source
as a nonexact reconstruction; no credit is granted, and register-forcing or
volatile tricks are barred.
### Waves 6-7, setup leaves and transition handlers
`actor_action_try_to_seek_cover` and `actor_action_try_to_panic` use a complete
`action_state_data` union, not a member-sized scratch buffer. The full union
is part of the original readable contract even though each setup helper only
writes its own member; it also reproduces January's stack frame and argument
layout. `actor_action_handle_pending_command_list` likewise needed the full
union and the original ordering: perform the immediate action first, then
clear the command-list reference. All three compiled strict-exact on the
first measured natural shape.
`actor_action_handle_done_fleeing`, `actor_action_handle_combat_failure`, and
`actor_action_handle_berserk_transition` were recovered from the mapped source
and January control flow. The fleeing handler retains the original `success`
assert and source line 2681. `actor_action_handle_combat_transition` also has
an Xbox-only tail absent from HCEX: when already in combat with status zero it
calls `actor_action_class` and deliberately discards the value. That call is
not dead trivia--it preserves the original range assertion. These four
handlers are all strict-exact.
`actor_action_handle_exit_pursuit` exposed a useful source-shape rule. Assigning
each action's done flag directly to the return variable made VC7 reuse `AL` for
the flag tests instead of January's `DL`. Calling lost-contact directly from
each successful case fixed the registers but allowed the compiler to combine
the preceding cdecl cleanup in the wait arm. The exact readable topology is:
test each action flag directly, set a one-byte `reported = TRUE` marker after
any perception notification, then make one common lost-contact call when the
marker is set. This preserves separate cleanup, keeps flag tests in `DL`, and
leaves the called result naturally in `AL`.
Strict result after Wave 7: **33/63 functions**, **4,208/19,172 code bytes**,
plus the exact actor-mode-name data owner. Every previously exact sibling
remained exact.
### `actor_action_handle_grenade_throwing` live residual
The January semantics and all seven relocation destinations are recovered.
Readiness requires a target type of at least five and rejects a fleeing actor
whose panic type is positive; the variant definition is fetched before those
checks. The two grenade-stimulus cases use combat status or the prop's swarm
state, and an already-armed throw is attempted after consideration. The best
readable body is 224 bytes versus January's 208. January keeps the result in
`BL`, reloads the actor index for calls, and emits a sign-extended short switch;
the current compiler keeps the actor index in `EBX`, spills the result byte,
and retains word comparisons. Moving the result and short declarations did
not change that allocation. The function remains nonexact and uncredited;
broader semantic work continues before any tie classification.
### Wave 8, vehicle and grenade leaves
`actor_action_try_to_enter_vehicle` initially gave VC7 a separate working
pointer for either the caller's seat list or the local fallback array. That
extra lifetime changed the January register assignment. Reusing the optional
parameter itself as the working pointer is both ordinary C and the natural
contract: when the pointer is null it is assigned the local array, otherwise
the caller-supplied count is used. Keeping the loop count short and sharing
the final false return then reproduced all 208 bytes and six relocations.
`actor_action_try_to_throw_grenade` required four semantic details that the
later HCEX body obscured. The damage rejection is spelled `!(damage > 0)` so
the original NaN path is preserved; the normalized direction must be positive;
the facing rejection is `!(dot < cosine)`; and the throw order sets the combat
flag before clearing the pending-plan flag. The minimum cosine is named from
its January float bits rather than replaced by a decimal approximation. The
result is strict-exact at 288 bytes and twelve relocations.
The neighboring `actor_action_consider_grenade` is reconstructed and has the
exact 288-byte size and all eleven relocations. Its remaining bytes are only
three references to one stack slot: January colors the base-chance temporary
at `[ebp-4]`, while this build colors it at `[ebp-8]`. Tested shapes include a
single combined expression, three named float temporaries, moving those
declarations outside the check, moving them before the last-check local, and
scoping the last-check local inside a nested block. The best readable shape
retains the named base/difficulty/result calculation and is not credited.
### Wave 9, exact vehicle-exit cluster
The mapped HCEX vehicle-exit topology incorrectly returned immediately when
the actor had no vehicle. January always clears `stimuli.vehicle_eviction`,
including that path, so the search is a guarded body followed by unconditional
cleanup. The first corrected shape reached exact size and relocation count,
but kept the successful `exited` value live in `BL` across `game_time_get`.
January calls `game_time_get`, stores the re-entry deadline, and only then sets
`exited = TRUE`. That source statement order lets VC7 return constant `AL=1`
on the successful path while retaining the false result slot for both failure
paths. The complete handler is strict-exact at 336 bytes and seven
relocations.
The adjacent private `code_0000b140` is the vehicle re-entry cooldown check.
It takes the actor index in the compiler-selected `EAX` private convention and
the vehicle index on the stack, returning false only while the requested
vehicle equals `vehicle_ignore_index` and the deadline is still in the future.
The direct readable predicate compiled strict-exact on its first build: 64
bytes and three relocations.
### `actor_action_handle_vehicle_entry` checkpoint residual
The full 960-byte January behavior is reconstructed, including both candidate
sources: friendly player-driven vehicle props and the 32-entry scripted
enterable-vehicle registry. The source-local registry layout is proven as
0x28 bytes per entry at `ai_globals+0x3B8`; no shared AI header was expanded.
The helper calls, object types, distances, team/type masks, packed AI-index
matching, and action setup give the exact 22 relocation destinations.
The first natural form was 960 bytes but mirrored the long-lived actor pointer
and actor index between `EBX` and `EDI`. Making the result a real named
success/failure value restored January's register roles. Further measured
changes were semantic rather than register forcing:
* spell the best-distance test as `!(distance < best)` to reject NaNs exactly;
* compare the packed encounter component through a dword xor plus `& 0xFFFF`,
rather than letting VC7 narrow the xor to a word;
* express AI scope selection as a switch, producing January's decrement chain;
* increment the long loop variables normally while comparing their short
views, avoiding extra sign extension; and
* assign the prop-derived squared distance before its vehicle index, allowing
the independent integer store to fill the x87 multiply window.
Those changes make the body instruction-identical over its substantive search
windows. The best checkpoint form is 944 bytes with all 22 relocations and
differs in the false-return topology: January materializes a zero byte in
`DL` for the three early guards and retains a separate late false epilogue;
this compiler folds both blocks despite equivalent named-result and explicit
label spellings. No barrier, volatile, register forcing, or control-flow byte
trick was attempted. The readable body stays as a nonexact reconstruction
and receives no credit.
## Checkpoint status before canonical reconciliation
The independent wave ends at **37/63 strict-exact functions** and
**4,760/19,172 meaningful January code bytes (24.83%)**. Those functions
occupy 5,104 padded section bytes; keeping the meaningful and padded measures
separate avoids overstating progress. The 16-byte/3-relocation
actor-mode-name data owner is also strict-exact. The 888-byte action dispatch
table remains deliberately disabled because callbacks in other unfinished
action translation units are not yet linkable; it receives no data credit.
The only shared-header changes are prototypes in `actions.h` and a
layout-neutral name for the already-existing byte at `ai_debug_state+8`.
`actors.h`, `ai.h`, `ai_communication.h`, `encounters.h`, `path.h`, and the
other headers concurrently used by the AI-debug lane were not modified.
## Canonical reconciliation and final checkpoint gates
The checkpoint was rebased onto canonical `a0bad121` before final validation.
The Actions object was then forcibly deleted and rebuilt with the unchanged
January campaign flags. The hardened semantic audit still finds exactly
37 strict functions (4,760 meaningful bytes), while direct `coff_compare`
confirms `_global_actor_mode_names` at 16 bytes, three relocations, and
normalized SHA-256
`374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb`.
The object remains `NonMatching`; no function, data, semantic, or parked
exception was added to configuration.
The fail-closed regression gate took a fresh Actions snapshot, deleted and
rebuilt the object again, and reported all 37 functions `still_exact`, with
zero changed nonexact functions, newly exact warnings, failures, ownership
warnings, or other warnings. Full `halobetacache_build` and `libcmt_build`
both passed after the rebase, so the layout-neutral header name and Actions
prototypes compile across every consumer. `progress` and `semantic_progress`
completed with 470 units scanned, 3,473 semantic-exact functions, 93 hidden
exact functions, and zero unit errors. The admission audit produced no new
completion candidate; this checkpoint intentionally receives no object-level
credit.
All **179/179** tool tests passed. `git diff --check` is clean, the config
diff is empty, and the added production code contains no assembly, volatile,
force/no-inline annotation, optimizer barrier, undefined-behavior trick,
byte forcing, compiler-flag change, or comparator change. Original behavior
that may look surprising is retained and explained above rather than silently
"fixed". This is therefore an honest readable-C foundation checkpoint, not
a claim that `actions.obj` is finished.
@@ -1,182 +0,0 @@
# `actions.obj` Codex closure ledger
This file continues the Actions investigation without modifying the earlier
Claude-authored `actions_obj.md`. January's object is always authoritative;
HCEX/PDB material is used only to recover names, types, and plausible source
topology. Every experiment below changes one readable-C factor, rebuilds the
whole translation unit, and receives no credit unless the hardened comparator
accepts the complete function and its relocation identities.
## Verified starting point
After a fresh rebuild, 55 of 63 functions are strict exact. Both owned data
sections are also exact: the combined 888-byte/96-relocation action table block
and the 16-byte/3-relocation actor-mode-name block. The eight remaining
functions have the correct target section size and relocation count:
- `code_0000b3b0`
- `actor_action_handle_vehicle_entry`
- `actor_action_handle_combat_selection`
- `actor_action_handle_lost_contact`
- `actor_action_try_to_dive`
- `actors_searching_same_position`
- `actor_action_handle_evasion`
- `actor_action_handle_danger_avoidance`
The reconstructed `actor_action_handle_surprise` must retain
`boolean result = FALSE;`. Removing the initializer loses an explicit January
zero materialization and regresses that previously exact function.
## Rejected closure experiments
### Vehicle entry: enclosing negative guard
The target has three result epilogues, so the setup tail was respelled as an
enclosing negative guard in an attempt to separate its late false exit from
the early named-result exit. The function stayed 960 bytes with 22
relocations, but the allocator and control-flow graph changed broadly: 240
normalized byte positions differed instead of the prior best 39 and several
call relocations moved. The prior readable best form was restored.
### Lost contact: HCEX local declaration order
The nested scalar declarations were reordered to match the later HCEX
CodeView record. This did not recover January's private helper convention or
frame coloring. `actor_action_handle_lost_contact` still had about 1,204
different positions, while `code_0000b3b0` stayed at 327; multiple relocation
sites shifted. The January-best declaration order was restored.
### Evasion: mixed direct and named-result exits
The return topology was rewritten to mirror the apparent January split between
direct success exits and named-result exits. VC7 instead cross-jumped the
blocks, shrinking the function from 800 to 784 bytes and worsening the overlap
to 513 differing positions with downstream relocation shifts. The prior
800-byte/27-relocation form was restored.
### Try to dive: authentic vector aggregate by itself
HCEX CodeView names a `real_vector2d evade_vector`, so the two scalar alignment
components were replaced with that aggregate without altering expressions or
control flow. Under external linkage this was codegen-neutral in the relevant
allocation: the function remained 720 bytes/29 relocations with 634 differing
positions and broad relocation-offset drift. The proven scalar form was
restored.
### Try to dive: static linkage by itself
HCEX CodeView says the original function was file-local. Making only the
definition `static` recovered January's important entry allocation exactly:
`actor_index` stayed in ESI and was copied to EDI for debug-array indexing.
However, the candidate then emitted only 704 bytes, retained 29 relocations,
and differed at 493 normalized positions. Its first remaining mismatch was
the actor spill at `[ebp-0x20]` rather than January's `[ebp-0x18]`; the switch
also merged two cases that January keeps separate. Static linkage was
therefore reverted. This is useful diagnostic evidence, not a match claim.
The target's external COFF symbol versus a static candidate is not by itself a
permanent blocker: this same object already has strict-exact file-local
functions whose csplit target symbol is external. The static experiment is
rejected because its bytes and section size miss, not merely because the COFF
storage-class numbers differ.
### Try to dive: scalar declaration order
This experiment retained external linkage and changed declaration order only.
January's stack homes are: best animation at `-0x8`, best score at
`-0xC`, best direction at `-0x10`, actor spill at `-0x18`, and the alignment
components at `-0x1C/-0x20`. The best candidate previously put the alignment
components first at `-0x8/-0xC`, pushing the later values downward. Therefore
the alignment declarations were moved after the three best-choice scalars.
A fresh whole-object build was byte-identical to the prior external baseline:
720 bytes, 29 relocations, normalized SHA beginning `e669dd20`, first mismatch
at `+0x0D`, and 634 differing overlap positions. The entry still destructively
used EDI and the actor spill remained at `-0x20`. VC7's coloring is invariant
to this declaration ordering, so the readable original order was restored.
### Combat selection: scalar declaration order
January places the cached prop distance at `[ebp-0x8]`, the melee charge delay
at `[ebp-0xC]`, and the actor-variant definition pointer at `[ebp-0x10]`.
The candidate instead cycles those values through `-0xC`, `-0x10`, and
`-0x8`. To test whether this was ordinary declaration coloring,
`prop_distance` and `charge_delay` were declared before the variant pointer;
the `REAL_MAX` assignment and every call remained at their previous execution
points. The rebuilt 1,776-byte/56-relocation function was unchanged in the
relevant allocation: the variant pointer remained at `-0x8`, prop distance at
`-0xC`, first mismatch at `+0x41`, and 1,013 normalized positions differed.
Later relocation sites also retained the broad allocation cascade. The
original readable declaration order was restored; simple declaration order is
not a control for this three-value cycle.
### Combat selection: direct last-attempt field
January keeps the current game time in EDI and does not load
`last_melee_check_time` until the comparison, while the candidate's named
`last_attempt_time` local caused that field to be hoisted into EDI. Removing
the local and spelling the two adjacent comparison reads directly from the
actor tested whether the field binding caused the register cascade. Instead,
VC7 shrank the function from 1,776 to 1,760 bytes, kept 56 relocations, moved
the first mismatch to `+0x6A`, and increased the overlap difference to about
1,200 positions. The desired local register change did not preserve the
surrounding control-flow/allocation topology. The named readable local was
restored.
## Frozen 55/63 checkpoint
The last rejected experiment was fully restored and a fresh build again
reports **55 of 63 functions strict exact**. This is the checkpoint source,
not a claim that `actions.obj` is complete. Each of the eight remaining
functions still has January's exact padded section size and relocation count;
their normalized bytes and/or symbolic relocation placement remain different,
so none receives credit. The combined 888-byte action-table/default-state
owner remains exact at 96 relocations, and the 16-byte actor-mode-name owner
remains exact at three relocations.
The bounded closing experiments above now answer the natural source questions
they were designed to test. In particular, simple declaration reordering did
not control the residual stack coloring, direct-field spelling caused a broad
allocation cascade, and topology rewrites changed section size or cross-jump
behavior. No independently supported readable-C factor remains ready for a
production experiment. Freezing the verified source is therefore safer than
turning register-allocation symptoms into speculative source churn.
The object intentionally stays `NonMatching`: `config.json`, the parked
ledger, compiler flags, comparator, and semantic-credit files are unchanged.
No assembly, volatile access, force/no-inline annotation, optimizer barrier,
undefined-behavior trick, type-punning byte force, or compiler-flag change was
used. January quirks are retained, including the original
`code_0000c970` assertion that does not validate
`escape_is_ledge_reference`. The separate Claude-authored
`actions_obj.md` was not edited, renamed, or deleted; this Codex-owned file is
the only new object log.
## Final checkpoint gates
The restored source was rebuilt again with the unchanged campaign compiler and
flags before the checkpoint audit. `research/actions_strict_status.py`
returned 55/63, and the hardened semantic report independently recorded those
same 55 functions at **10,906 of 19,172 meaningful January code bytes
(56.89%)** and 11,392 padded bytes. The eight residual rows above retain
their exact January padded sizes and relocation counts. Direct hardened COFF
comparison also reconfirmed both data owners: the 888-byte/96-relocation
combined block has normalized SHA-256
`b4bd37bc3750a724b625c0254a2b8bc3272c1aff70c71ae46952bac6acd4a9ab`,
and the 16-byte/3-relocation name block has normalized SHA-256
`374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb`.
The full Halo and libcmt build targets pass. Fresh progress and semantic
generation scanned 470 units with zero unit errors; the admission audit found
no Actions completion candidate and granted no credit. All 179 tool tests
pass. `git diff --check`, the config/parked/semantic-credit diff, the
forbidden-construct scan, and the Claude-Markdown preservation audit are
clean.
The formal regression-manifest snapshot is the sole administrative gate not
recorded in this worktree. It correctly refuses to snapshot a dirty tree, and
the desktop Git metadata ACL/approval-service usage limit prevented creation
of the local checkpoint commit needed to make the tree clean. The forced
Actions rebuild and strict whole-object sweep itself did pass at 55/63; the
lane is left commit-ready so the clean-commit snapshot/check can be run as
soon as Git metadata writes are available. This limitation does not turn the
checkpoint into object-level credit.
@@ -1,144 +0,0 @@
# Actions large natural-C pair and no-fake audit (2026-08-30)
## Scope
This wave starts from canonical commit
`fa102b086bebdf6f874145025a05323c8f9d9d3f` and changes only the large
`source/ai/actions` reconstruction plus the headers that own its shared enums
and prototypes. The pre-wave Actions gate was 55/63 strict-exact functions.
The source frontier came from the Fable history rather than a wholesale
cherry-pick:
- commit `79275995c484a2637aa5db570030d42a9e02685d`, source blob
`d8d32fb630fb70e6bc8733efd97fcd69b31c397f`, supplied the combat-selection
and first evasion candidates;
- commit `c8ff585ee941bd8454e991d7512b5b36ba0f161a`, source blob
`92eb33aa4cd3e94a2dda31edf6bf9063faf59de1`, supplied the danger-avoidance
experiment;
- the later house-rule commit
`2a9ede1cc37821492fe56ddbecb5898f60073605`, source blob
`bc1ceba344612a50a191e36f2fda1d8d47d0242f`, is controlling evidence for
the no-fake review below.
No donor commit was transplanted wholesale.
## Accepted strict functions
`_actor_action_handle_combat_selection` is strict-exact:
- 1,776 padded bytes;
- 56 relocations with identical addresses, types, and destinations;
- normalized SHA-256
`2fb7b29eea215e53086b6ce6fd971c3eb4ed361fa45f1c836d3e4833c2edf5ed`.
The unusual calls were reviewed rather than accepted merely because they
match. The independent target lift at
`research-cache/stian-halo-current-20260827/src/halo/ai/actions.c` lines
2461-2713 proves the controlled-unit type-verification call and the discarded
`actor_has_ranged_weapon` call before melee charge setup. Both calls therefore
express original behavior, including their validation/side effects; neither is
an inert compiler lever. The same lift corroborates the vehicle cooldown and
lookup order, charge-state policy, and final assertion ladder. Negated real
comparisons retain the January x87 unordered/NaN behavior and are not Boolean
noise.
`_actor_action_handle_evasion` is strict-exact:
- 800 padded bytes;
- 27 relocations with identical addresses, types, and destinations;
- normalized SHA-256
`f38b2a0650790ee8d848ca0a4faee7a8cee3c00e2ad3048412b62c8db594370b`.
The admitted source is ordinary structured C: the first successful cover or
panic action sets `result`, and the remaining policy is guarded by
`if (!result)`. The perceived-danger branch is positive, later successes only
set `result`, and the function has one terminal return. There is no donor
`goto`, label, assembly, volatile state, wrapper, optimizer directive, object
patch, or undefined behavior. Direct-return, compound-guard, and nested
single-exit forms were measured; the nested single-exit form is both natural
and uniquely strict. `boolean result;` followed by `result = FALSE;` is kept as
an evidence-backed exception to the declaration/assignment preference: a
same-line initializer changed 657 bytes and the relocation schedule.
The accepted gain is therefore two functions and 2,576 padded code bytes:
55/63 to 57/63.
## Honest rejection of a coincidental exact result
`_actor_action_handle_danger_avoidance` is deliberately **not** accepted.
The donor's only strict form changed the ordinary condition
```c
if (!body_within_danger)
```
into an empty positive arm followed by the real work in `else`. Fable commit
`2a9ede1cc` independently identifies that shape as an exact-only fake-match
generator and reverts it. This wave keeps the coherent negative condition.
The result is honestly residual at 1,504 candidate bytes versus the 1,520-byte
January function, with 34 relocations on both sides and candidate normalized
SHA-256
`b1d5ecaf33b392f8b6071a515b226a8c41e112097105096e0216f9554e3b328f`.
No credit, exception, adjudication, or park is added for it.
## Private helper and inline ownership
The private `_code_0000c970` placeholder is renamed
`actor_action_find_escape_from_danger` and remains `static`. HCEA PDB/cross-
build correspondence and the January-addressed function atlas both provide
that original name; this is not an address-derived invention.
An initial exact experiment manually reproduced the formal parameter aliases
of `point_from_line3d`. It was rejected under the no-fake rule because those
aliases conveyed no policy and existed only to control compiler scheduling.
Production instead uses six plain coordinate assignments, an established
project idiom. This ordinary form remains strict-exact at 928 bytes, 28
relocations, and normalized SHA-256
`ec0b698df2d40432b1b2a912ebc33eca0a989f2420cc69d3b184ab8131a34e11`.
The rebuilt object owns no symbol containing `point_from_line3d`, preserving
the January inline/COMDAT boundary without fake source.
## Type, enum, and prototype ownership
- `destination_type`, `actor_vehicle_driver_type`, and the danger-zone enums
live in `actors.h`, their closest shared owner.
- `action_class` lives in `actions.h`.
- the complete communication type/hostility enums and
`ai_communication_event` declaration live in `ai_communication.h`.
- local numeric switch/action/hostility values were replaced with the matching
enum constants.
- the duplicate local danger enum in `actor_perception.c` and duplicate local
communication prototype in `actions.c` were removed.
- all real-valued locals use `real`; typed object access uses the appropriate
`unit_get`/`vehicle_get` paths.
## Verification
The final rebuilt Actions gate reports 57 exact, six residual, and zero
unwritten functions. The six residuals are the honest danger-avoidance form
plus the five inherited residuals:
- `_actor_action_handle_lost_contact`;
- `_actor_action_handle_vehicle_entry`;
- `_actor_action_try_to_dive`;
- `_actors_searching_same_position`;
- `_code_0000b3b0`.
The full `halobetacache_build` and `libcmt_build` targets pass. Consolidated
`progress` and `semantic_progress` pass with 470 units scanned, 4,986 functions
evaluated, 4,876 semantic-exact functions, 131 hidden exact functions, 4,886
accepted exact functions, and zero unit errors. All 212 tool tests pass.
`tools.parked_functions` reports 13 active, zero stale, and zero invalid
entries. `git diff --check` is clean.
Claude's prototype fake-smell scanner reports no smell in `actions.c`. Its
three reports are pre-existing empty-then forms in `actor_perception.c`, outside
this Actions admission and unchanged by this wave. They receive no new credit.
The old pre-wave regression manifest is not used to waive failures: moving
enums/prototypes to their owning headers and applying the supported private
symbol rename intentionally changes compiler-private/debug ownership. Strict
function comparison proves exactly two accepted gains and no loss among the 55
pre-wave exact functions. A clean committed-state Actions snapshot and forced
rebuild/check are captured after this ledger is committed.
@@ -1,116 +0,0 @@
# `actions.obj` policy-reconciled replay (2026-08-27)
## Result
The preserved Actions reconstruction was replayed into the integration tree and
reconciled with the newer shared headers and house rules.
- strict functions: **0/63 -> 55/63**
- strict padded function bytes: **0 -> 11,392**
- unwritten owners: **62 -> 0**
- residual owners: **1 -> 8**
- `_global_action_functions`: **888 bytes / 96 relocations, exact**
- normalized SHA-256:
`b4bd37bc3750a724b625c0254a2b8bc3272c1aff70c71ae46952bac6acd4a9ab`
- `_global_actor_mode_names`: **16 bytes / 3 relocations, exact**
- normalized SHA-256:
`374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb`
The strict project board after a forced 569-object rebuild is:
```text
objects 270/619
functions 4445/8246
bytes 592087/1922669
```
The forced rebuild exposed eight additional exact `units.obj` functions from
already-committed Units source. Those 5,808 bytes are not credited to this
Actions replay. Actions itself contributes 55 functions and 11,392 bytes.
## Provenance read before integration
- donor tree:
`C:\Users\isabe\Documents\Codex\2026-07-13\i-w\work\actions-full-20260813`
- preserved Claude ledger:
`docs/object_matching_logs/actions_obj.md`
- preserved Codex closure ledger:
`docs/object_matching_logs/actions_obj_codex_closure.md`
- historical integration precedent:
`66112c6181b2e882e3382e739aaeb528606d4285`
- house rules source:
`C:\Users\isabe\Documents\Codex\2026-07-13\i-w\units-complete-20260824\docs\house_rules.md`
- house rules SHA-256:
`9294B81DC2E1C92D75FFD2F9F289287131BDBA3564BA95BB054B08189781BFE1`
The two preserved ledgers remain verbatim. This file records only the
integration-specific reconciliation and verification.
## Policy and header reconciliation
The donor's raw byte-offset accessors and private cast overlays were not copied
into production. They were replaced with typed source constructs whose layouts
are compile-time pinned:
- `prop_datum` reads use the canonical `enemy` and `dead` members at `+0x60`
and `+0x127`.
- `actor_datum` movement reads use `control.moving` at `+0x504`.
- the donor's `unit_definition + 0x238` access was identified as
`unit.dive_distance`, not `unit.evade_distance`; the nested offset sum is
asserted as `0x238`.
- the two vehicle AI values use a TU-local completion of the already-declared
`struct vehicle_definition`, returned directly by the typed
`vehicle_specific_definition_get` macro. Their offsets are pinned at
`0x390` and `0x394`.
- debug writes use the canonical `struct actor_debug_info`. The mapped cover,
grenade, danger, and dive clusters are pinned at `0xB8`, `0x150`, `0x168`,
and `0x184`; the complete record is pinned at `0x657C`.
- the image-attested `ai_debug.flee_always` byte at `+0x8` replaces the donor's
incompatible private view without changing `struct ai_debug_state` size.
- the Actions view of `ai_globals` pins `grenades_enabled` at `0x3B4`, the
enterable-vehicle array at `0x3B8`, and the complete local prefix at `0x8B8`.
The newer Units lane intentionally declares the animation-impulse argument as
`long`. Keeping the reconstructed evade animation local as `long` preserves
January's full-register call shape and the exact 464-byte
`actor_action_try_to_evade` owner without changing Claude's active Units header.
All reconstructed declarations use one parameter per line, every no-argument
list places `void` on its own line, and every reconstructed `void` definition
has an explicit terminal `return;`.
## Strict residual frontier
```text
1776 actor_action_handle_combat_selection
1520 actor_action_handle_danger_avoidance
800 actor_action_handle_evasion
1872 actor_action_handle_lost_contact
960 actor_action_handle_vehicle_entry
720 actor_action_try_to_dive (candidate 704)
320 actors_searching_same_position
368 code_0000b3b0
```
No residual is credited. The preserved donor ledger contains the exhaustive
negative matrices and the best measured shapes for these eight owners.
## Verification
```text
build\local-tools\ninja.exe -t clean all_source
build\local-tools\ninja.exe all_source
python -B tools/campaign/verdicts.py scratch/actions_replay_after.json
python -B tools/campaign/verdicts.py --diff \
scratch/actions_replay_before.json scratch/actions_replay_after.json
python -B tools/campaign/gate.py source/ai/actions
python -B tools/campaign/gate.py source/ai/ai_debug
python -B tools/campaign/gate.py source/ai/ai
python -B tools/campaign/gate.py source/units/units
python -B tools/campaign/board.py
```
The before/after verdict diff reports **63 gains and 0 regressions**. Actions
gates at **55 exact / 8 residual / 0 unwritten**. `ai.obj` remains 13 exact
with no residual owners, `ai_debug.obj` retains all 11 prior exact owners, and
`units.obj` gates at its committed **183 exact / 6 residual / 0 unwritten**.
@@ -1,20 +0,0 @@
# `actions.obj` pursuit-helper name recovery (2026-08-31)
The January symbol atlas and HCEA release PDB recover the private helper at
file offset `0xB3B0` as `actor_action_determine_pursuit_options`, with static
linkage. The source, caller, symbol atlas comment, and `config/symbols.json`
now use that name instead of `code_0000b3b0`.
Claude's experimental removal of the `controlled_by_group` formal was not
retained. The later PDB authenticates the complete interface, and deleting a
real parameter merely changed the private register convention. The parameter
and its call argument remain. The artificial explanatory `(void)` use was
removed because it had no semantic or machine-code role and claimed more
January provenance than the evidence supports.
This is a naming/authenticity correction, not byte-match credit. The helper
remains an honest 368-byte, six-relocation residual (98.24428% objdiff), and
`actions.obj` remains 57 exact / 6 residual / 0 unwritten. The existing
Actions closure ledgers already record the bounded source experiments; no
register-forcing construct, ABI lie, or speculative parameter deletion is
introduced here.
@@ -1,83 +0,0 @@
# `actions.obj` large fuzzy closeout (2026-09-01)
## Result
The canonical `source/ai/actions` reconstruction remains the strongest known
source frontier: **58/63 strict-exact functions, five residual functions, and
zero unwritten functions**. No production C changed in this pass. All five
remaining bodies are now evidence-backed fuzzy parks under the current house
rule, so future exact work requires genuinely new provenance or a natural
same-compiler code-generation lever.
The five honest residuals account for 5,392 January padded bytes:
| Function | Target | Candidate | Relocations | Fuzzy |
| --- | ---: | ---: | ---: | ---: |
| `_actor_action_handle_vehicle_entry` | 960 | 960 | 22 / 22 | 96.54422% |
| `_actor_action_handle_lost_contact` | 1,872 | 1,872 | 55 / 55 | 95.328384% |
| `_actor_action_handle_danger_avoidance` | 1,520 | 1,504 | 34 / 34 | 90.41458% |
| `_actor_action_try_to_dive` | 720 | 704 | 29 / 29 | 85.18107% |
| `_actors_searching_same_position` | 320 | 320 | 14 / 14 | 87.87611% |
These parks do not claim strict byte credit. They classify completed semantic
source whose remaining differences have resisted credible source-level
control.
## Evidence reviewed
Before parking, this pass read the preserved Claude, Fable, and integration
records:
- `docs/object_matching_logs/actions_obj.md`;
- `docs/object_matching_logs/actions_obj_codex_closure.md`;
- `docs/object_matching_logs/actions_obj_jonas_policy_reconciled_replay_20260827.md`;
- `docs/object_matching_logs/actions_obj_jonas_large_natural_pair_no_fake_20260830.md`;
- `docs/object_matching_logs/actions_obj_jonas_pursuit_name_recovery_20260831.md`;
- `docs/object_matching_logs/actions_obj_semantic_private_names_20260831.md`;
- the campaign throughput and methodology records.
`tools/campaign/branch_sweep.py source/ai/actions` compiled nine unique source
blobs reachable from the registered campaign branches. None exceeded the
canonical 58-exact/5-residual/0-unwritten frontier. Older blobs either failed
against the current headers or regressed to 54 or fewer exact functions.
## Anti-fake ruling
The only known strict form of `_actor_action_handle_danger_avoidance` uses an
empty positive `if` arm followed by all meaningful work in `else`. That form
was independently identified as an exact-score-only topology and previously
reverted. It remains rejected. The retained production source uses the
coherent negative predicate, accepts the honest 16-byte envelope difference,
and contains no volatile/register steering, barriers, pragmas, artificial
dependencies, inert carriers, undefined behavior, or nonsensical branches.
The other four retained bodies are likewise ordinary typed C. Their residuals
are compiler allocation, private convention, frame coloring, instruction
scheduling, or translation-unit-context differences documented in the parked
manifest. No inline assembly is introduced, and the object emits no
`point_from_line3d` COMDAT.
## Reopen conditions
Reopen a parked function only for one of:
- authenticated January source or local-variable records;
- a compatible original compiler/QFE that naturally emits the target;
- a natural same-compiler donor with the same control-flow and lifetime shape;
- new cross-build provenance that contradicts the retained semantics.
Pure score steering is not new evidence.
## Verification
The admission commands for this closeout are:
```text
python -B tools/campaign/gate.py source/ai/actions
python -B -m tools.parked_functions
python -B tools/fake_match_scan.py source/ai/actions.c
git diff --check
```
The final integration wave must additionally run the full canonical Ninja,
semantic, regression, test, and COMDAT checks before publishing.
@@ -1,145 +0,0 @@
# `actions.obj` opus5 100K wave D: park re-investigation (2026-09-14)
## Scope and provenance
- Translation unit `source/ai/actions.c`, target `build/split/source/ai/actions.obj`
(January 2002 Xbox debug build, XDK 3911 / VC7 13.00.9254, repository flags
`/O2 /Oy- /DDEBUG /Dxbox`, unchanged).
- Lane worktree `C:\halo-worktrees\opus5-100k-consolidated-20260914`, wave D
(park re-investigation, brief sections 9-10). Worker notes:
`scratch/workersD/actions.md`; scratch candidates in `scratch/workersD/actions/`.
- The prepared Ghidra and map-atlas files for actions.obj did not exist, so the
evidence was the January COFF (alndiff, relocations, frame census), the HCEA and
Stian lifts (names and behavior only), and **new** HCEX.pdb (SHIP) local-variable
records read with `DIA2Dump.exe -sym <function>`.
- Ledgers read first: `actions_obj.md`, `actions_obj_codex_closure.md`,
`actions_obj_jonas_policy_reconciled_replay_20260827.md`,
`actions_obj_jonas_large_natural_pair_no_fake_20260830.md`,
`actions_obj_jonas_pursuit_name_recovery_20260831.md`,
`actions_obj_semantic_private_names_20260831.md`,
`actions_obj_large_fuzzy_closeout_20260901.md`, plus the five `config/parked.json`
entries.
## Validated baseline (lane HEAD)
`== exact 58 residual 5 unwritten 0 (of 63 listed)`, and all five residuals are
parked (locked):
| Function | Target | Base | Relocs | Base normalized sha16 |
| --- | ---: | ---: | ---: | --- |
| `_actor_action_handle_danger_avoidance` | 1520 | 1504 | 34/34 | b1d5ecaf33b392f8 |
| `_actor_action_handle_lost_contact` | 1872 | 1872 | 55/55 | 0e96d40ea2f38122 |
| `_actor_action_handle_vehicle_entry` | 960 | 960 | 22/22 | 80e7bf257e70635f |
| `_actor_action_try_to_dive` | 720 | 704 | 29/29 | 1489a21aa830ee03 |
| `_actors_searching_same_position` | 320 | 320 | 14/14 | 844a679f805ae87f |
## Result
The production file is **unchanged** (every target is parked). Two parks were made
strict EXACT in scratch with natural source and are proposed for re-opening. The
combined candidate `scratch/workersD/actions/c2.c` (diff
`scratch/workersD/actions/park_reopen_c2.diff`) gates at
**60 exact / 3 residual / 0 unwritten**: every baseline EXACT row stays EXACT, the
`_point_from_line3d` guard passes, the three remaining parks keep their
`measurements.base` size/relocations/sha, the owner census against
`build/base` differs only in the danger-avoidance size and `.debug$S`
(`__FILE__` artifact), and `tools/fake_match_scan.py` reports 0 leads.
### `_actor_action_handle_danger_avoidance` - scratch strict EXACT (1520/34, sha 50e4161c4fc3e7ba)
- January facts: the frame is `sub esp,0xac` (base `0xb0`); January reuses the
`actor_index` home `[ebp+8]` for the ledge distance and `[ebp+0xb]` for
`escape_is_ledge` because `actor_index` is in EBX from `find_escape` onward, and it
places the outside-zone diagnostic block at +0x2c2 as the fall-through of the
`path_re_enters_danger = FALSE` store, reached by a backward `je` from the body
test.
- Source change (one shape): the outside-zone record became the `else` arm of
`if (body_within_danger) { urgency, communication, escape and dive handling } else { outside-zone diagnostic }`
instead of `if (!body_within_danger) { ...; goto try_avoid_action; }` followed by the handling.
- This is **not** the previously rejected empty-then form: the then-arm holds all of
the avoidance work, there is no empty arm and no inverted predicate.
### `_actor_action_handle_lost_contact` - scratch strict EXACT (1872/55, sha 1b2dca7a160e3658)
New provenance: the HCEX.pdb local records for this function group the pursuit
desire locals (`is_pursuit_controller`, `group_pursuit_restriction`,
`when_to_search_at_target`, `controlled_by_group_pursuit`, `when_to_pursue`,
`when_to_search_pursuit`, `controlling_group_pursuit`) in a nested block. They also
put `evaluation_context`, `new_firing_position`, `path_state_valid` and
`previous_owner` in one inner block, and give each action change its own block-local
`new_state_data`. The January byte facts agree:
| Source change | January evidence |
| --- | --- |
| desire locals into the `if (!orphan ...)` block | `pursuit_from_uncover` shares frame cell -5 with `controlled_by_group_pursuit` (sibling scopes) |
| `pursue_tenacious` declared first | init store order starts with `[ebp-0x12]` |
| `firing_position_index` / `pursuit_from_uncover` declared in `if (allow_pursuit)` | `or edi,-1` / `mov [ebp-5],0` emitted after the `allow_pursuit` test |
| no `search_started` local (call results tested directly) | no slot, `test al,al` on the call result |
| `search`, `candidate`, `previous_owner_actor_index`, `position_flags` (uninitialised out-parameters) declared in the select block | no init stores; the last store-schedule swap disappears only with this scoping |
| `orphan == NULL ? NONE : orphan->last_perceived_time` (both sites) | NONE arm is the fall-through at both sites |
| block-local `last_perceived_time` before the encounter test | value computed before `cmp encounter_index,-1` |
| `actor->firing_positions.` direct access, count reset before prop index | count store precedes the CSE'd prop store; a `firing_positions->` pointer store kills the CSE (measured 1888 bytes) |
Ablation in the final context: restoring the original
`find_path_direction_from_target = actor->target.target_prop_index != NONE` stays
EXACT (kept). Reverting the polarity, the block-local time, or the count-first order
each loses exactness. Restoring the pointer local breaks the size.
## Experiment matrix for the remaining parks (scratch only, all reverted)
| Function | Shape | Result |
| --- | --- | --- |
| searching_same_position | A single-exit if/else-if chain | 116 insns, same allocation, loses an epilogue |
| searching_same_position | B `result = (distance < 0.49)` | identical to A |
| searching_same_position | C base CFG with `return result` | 123 insns, allocation unchanged |
| searching_same_position | D declare+init + C | identical to base |
| vehicle_entry | V3 enclosing positive guard | identical to base |
| vehicle_entry | V1/V2 literal success returns (diagnostic) | result const-folded, 944 bytes |
| vehicle_entry | V4 `result = TRUE; goto result_exit;` (diagnostic) | identical to base |
| vehicle_entry | V5 block-local `new_state_data` (HCEX.pdb) | identical to base |
| try_to_dive | D2 HCEX locals, `real_vector2d evade_vector`, if/else tail, no `local_actor_index` copy | 704; evade-vector slots and tail now match; still not inlined |
| try_to_dive | D3 perpendicular scalars, i-first dots | 720 size, x87 load order still lazy |
| try_to_dive | D3c scalars before best_* inits | identical to D3 |
| try_to_dive | probe: a `short animation_impulse` prototype (diagnostic, not landable) | reproduces January's 16-bit impulse push exactly |
## Residual classification (remaining parks)
- `_actors_searching_same_position`: register allocation decided before `result`'s first
definition (actor pointer EBX vs spill, ECX/EDX swap of the second pursuit location).
January returns the result byte on every path. No single-exit, paren or inline law
moves it.
- `_actor_action_handle_vehicle_entry`: three differences only. January keeps an
unfolded zero-materialised `result` (DL) for the three guard exits while its success
path returns constant 1; base cross-jumps the guard exit into the success tail. There
is also one integer store one x87 instruction early. Same family as the
zero-register-pinning residual.
- `_actor_action_try_to_dive`: entry/parameter register allocation (January keeps
`actor_index` in ESI and reloads `alignment_vector` per case), perpendicular x87 load
order, and a 16-bit `unit_test_animation_impulse` argument. HCEX.pdb gives
`(long unit_index, short animation_impulse)`, but units.c's exact definition keeps a
full-width parameter, so this is a header-prototype question.
## Do-not-repeat
- The empty-then `if (body_within_danger) {} else {...}` form (fake; superseded by the
structured if/else above).
- Declaration-order-only and pointer-local experiments on lost_contact (the residual was
scoping and aliasing, now closed).
- Tail spellings V1-V5 on vehicle_entry; shapes A-D on searching_same_position.
- Scalar perpendicular locals on dive (VC7 forwards them lazily).
## Reopen criteria
- vehicle_entry / searching_same_position: authoritative January locals or a natural
same-compiler donor for the guard-exit zero or the allocation (HCEX.pdb has no stack
locals for searching).
- try_to_dive: an orchestrator decision on the `short animation_impulse` prototype
seen by actions.c (the declaration-count law applies to units.h), then a natural form
for the perpendicular scores and the entry allocation.
## Disposition
- Production `source/ai/actions.c`: unchanged, 58/63 strict exact, five parks intact.
- Proposed park reopen (orchestrator lands in a dedicated commit):
`_actor_action_handle_danger_avoidance` and `_actor_action_handle_lost_contact` from
`scratch/workersD/actions/c2.c`.
@@ -1,84 +0,0 @@
# `actions.obj` opus5 150K wave 2: park triage (2026-09-14)
## Scope
- Translation unit: `source/ai/actions.c`.
- Worker notes: `scratch/workers/actions_action_flee_etc.md`.
- Ledgers read:
- `actions_obj.md`
- `actions_obj_codex_closure.md`
- `actions_obj_jonas_policy_reconciled_replay_20260827.md`
- `actions_obj_jonas_large_natural_pair_no_fake_20260830.md`
- `actions_obj_jonas_pursuit_name_recovery_20260831.md`
- `actions_obj_semantic_private_names_20260831.md`
- `actions_obj_large_fuzzy_closeout_20260901.md`
- `actions_obj_opus5_100k_waveD_20260914.md`
- 100K lane worker notes also read: `scratch/workersD/actions.md` (shapes A-D, V1-V5, D2/D3/D3c/D3probe).
- Inherited exact functions, not touched: `_actor_action_handle_lost_contact` and `_actor_action_handle_danger_avoidance`.
## Baseline and result
The baseline and the final gate are identical: `== exact 60 residual 3 unwritten 0`. The guard passes and park drift is 0. The production file is **unchanged**.
| Function | Outcome | Reason |
| --- | --- | --- |
| `_actor_action_try_to_dive` (720) | OWNER-BLOCKED | January pushes a 16-bit `animation_impulse` (+0x185 `xor ecx,ecx; mov cx,[esi-2]`). HCEX.pdb gives `unit_test_animation_impulse(long, short)`, but `units.h:638` declares `long`, and the definition is in protected `units.c`. Any probe has to rename the callee symbol, so a strict measurement is impossible. The prerequisite is the 100K lane's units.h + units.c proposal. |
| `_actor_action_handle_vehicle_entry` (960) | NOT-LANDED (scratch closer body) | See the vehicle-entry section below. |
| `_actors_searching_same_position` (320) | SKIPPED-EXHAUSTED | HCEX.pdb has parameters only. The function is static in HCEX but public in the January PDB publics (rva 59216). Shapes A-D already cover the single-exit and result forms. |
## `_actor_action_handle_vehicle_entry`: single-exit re-investigation
Scratch only. The best candidate is `scratch/workers/actions_action_flee_etc/act_ve3.c` (diff: `act_ve3.diff`).
### Lever
The lever comes from this wave's flood measurements. VC7 splits a return join into constant epilogues where each path's value is a known constant. It keeps the live register copy (`dl`) only on paths where the initialiser is still enregistered.
### Shapes
**VE3.** A fully structured single exit (SINGLE-EXIT LAW), with no gotos or labels:
```c
if (!(flee && panic_type > 0) && action != obey && !(last_vehicle_check_time != NONE && last_vehicle_check_time + 45 >= current_time))
{
...
if (best_vehicle_index == NONE && certain_combat_timer >= 60)
{
for (slot = 0; ...) { ... }
}
if (best_vehicle_index != NONE && action_vehicle_setup_impromptu(...))
{
actor_action_change(...);
result = TRUE;
}
}
return result;
```
Result: 960/22, sha 9139bfc47fc7de8a.
- The success tail is now January's `mov al,1`. The previous build merged it as `mov dl,1 ... mov al,dl`.
- Three hunks remain. Two are the FALSE epilogue copies in swapped order: January places the late-exit `xor al,al` copy first and the guard `mov al,dl` copy last, while our build does the reverse. The third is the prop-loop `best_vehicle_index` store, emitted one x87 instruction before January's position (before `fmul st(1)` instead of after it).
**Other shapes:**
| Shape | Result |
| --- | --- |
| VE1: the late `return FALSE` statements jump to the common return | identical to VE3 |
| VE4: `result` declared first | worse |
| VE5: De Morgan'd guard | identical to VE3 |
| VE6: `best_vehicle_index` stored before the distance | identical to VE3 |
### Outcome
The function is not exact, so the park stays. The whole-TU rows are unchanged, the other parks are OK, the guard passes, and the fake scan reports 0 leads.
**Reopen criterion:** start from `act_ve3.c`. A source fact is still needed that orders the two FALSE epilogue copies and schedules the integer store after the x87 multiply.
## Checks
On the real file:
- owner census: no new or dropped owners;
- `fake_match_scan`: 0 leads;
- `git diff --stat -- source/ai/actions.c`: empty.
@@ -1,145 +0,0 @@
# `actions.obj` opus5 150K wave 3: law-detector pass over the three parks (2026-09-14)
## Scope
- Translation unit `source/ai/actions.c`; target `build/split/source/ai/actions.obj`.
- Worker notes: `scratch/workers/w3_actions_path_obstacle_avoidance_etc.md`; candidates in
`scratch/workers/w3_actions_path_obstacle_avoidance_etc/`.
- Ledgers read: `actions_obj.md`, `actions_obj_codex_closure.md`,
`actions_obj_jonas_policy_reconciled_replay_20260827.md`,
`actions_obj_jonas_large_natural_pair_no_fake_20260830.md`,
`actions_obj_jonas_pursuit_name_recovery_20260831.md`,
`actions_obj_semantic_private_names_20260831.md`, `actions_obj_large_fuzzy_closeout_20260901.md`,
`actions_obj_opus5_100k_waveD_20260914.md`, `actions_obj_opus5_150k_w2_20260914.md`, the 100K lane
`scratch/workersD/actions.md` (with its D3probe and searching_A-D bodies), the w2 worker notes, and the
actors / ai_communication ledgers that carry the pending P1 `short animation_impulse` packet.
- Inherited exact functions, not touched: `_actor_action_handle_lost_contact`,
`_actor_action_handle_danger_avoidance`.
## Baseline and result
Baseline and final real-file gate are identical: `== exact 60 residual 3 unwritten 0`, guard passes,
park drift 0, owner census clean, `fake_match_scan` 0 leads. The production file is **unchanged**
(`git diff --stat` empty).
| Function | Padded | Outcome |
| --- | ---: | --- |
| `_actor_action_try_to_dive` | 720 | **PARK-REOPEN-PROPOSED**: strict EXACT in scratch under the pending P1 units.h prototype |
| `_actor_action_handle_vehicle_entry` | 960 | NOT-LANDED (2 new shapes, no lever) |
| `_actors_searching_same_position` | 320 | NOT-LANDED (one A10 detector shape, worse) |
## `_actor_action_try_to_dive`: strict EXACT pending P1
### Measurement method (new)
w2 recorded this function as unmeasurable because every probe had to rename the callee. This wave used a
shadow header tree instead: `shadow_short/units/units.h` is the current `units.h` with exactly the P1 change
(`short animation_impulse` in `unit_test_animation_impulse` and `unit_start_animation_impulse`), placed first on
the include path by `shadow_gate.py`. The callee keeps its real symbol, so `section_infos_equal` is strict.
### January evidence and the laws that explain it
1. **A4 helper routing.** Escape-direction cases 2 and 3 are `fld [eax+4]; mov ecx,[eax]; fstp [ebp-0x1c];
mov [ebp-0x20],ecx`. Explicit component copies give four integer moves. An inlined
`set_real_vector2d(&evade_vector, alignment_vector->i, alignment_vector->j)` reproduces the x87 form. The
callee `actor_moving.c:1343-1360` spells the identical evasion switch with `set_real_vector2d`.
2. **A4 + A22 dot canonicalisation.** `fld facing.j; fchs; fld facing.i` happen before the first dot product
and are consumed later by `fmul; fxch; fmul; faddp`. That is a left-perpendicular vector built by one
`set_real_vector2d(&left, -facing.j, facing.i)` and two `dot_product2d` calls (j term first).
3. **Single-exit law.** January keeps `actor_index` in ESI (`mov edi,esi` for the debug index) and reloads
`alignment_vector` from its parameter slot in every case. The whole dive therefore sits in the
lower-frequency body of `if (vehicle_index == NONE && actor_move_try_evasion_direction(...))`, with an
`else { cannot_move; result = FALSE; }` arm. Inside it, `if (best_animation == NONE) {...} else {...}` sets
`result`, and the function has one `return result;`.
4. **P1.** The impulse push `xor ecx,ecx; mov cx,[esi-2]` is a 16-bit prototype parameter. HCEX.pdb has
`unit_test_animation_impulse(long, short)`.
### Shapes
All shapes were measured under the shadow P1 header.
| Shape | Result |
| --- | --- |
| dv0: D3probe body with the real callee name | 720/29 sha; entry allocation, case 2/3 copies and scores differ |
| dv1: + `set_real_vector2d` evasion switch | cases 2/3 now January's x87 form |
| dv_s2 / dv_s3: + `left` vector (explicit or `dot_product2d`, byte-identical) | score block equal |
| dv_e1: + single-exit nesting | **strict EXACT**, 61 exact |
| dv_f: block-scoped declare+init moved before the switch | worse (60), reverted |
| dv_f2 / dive_reopen.c: dv_e1 + enum constants, `left` at function scope | **strict EXACT**; the bare block was measured inert and dropped |
The enum constants are `_actor_evade_left/right/forward/back` and `_ai_communication_dive` (= 44, counted in
`ai_communication.h`).
### Candidate
`scratch/workers/w3_actions_path_obstacle_avoidance_etc/dive_reopen.c`, diff `dive_reopen.diff`. It is the
current real file plus:
- the new dive body;
- the TU-local `enum actor_evade_direction` completed with `_actor_evade_forward` and `_actor_evade_back`
(values 2 and 3, as in the complete copy in `actor_moving.c`; `_actor_evade_random` keeps value 4), with a
disclosure comment naming the other copy. No shared owner header declares this enum.
Measurements:
- **Shadow P1:** 720/29, sha `2548ce8b22f46d48` == target; 61/63 exact. The other two parks are unchanged,
and `danger_avoidance`'s `unit_test_animation_impulse` call stays EXACT under the short prototype.
- **Current headers:** 720/29, sha `b2b7a2731165dff9`. The only code hunk is `movsx ecx,word [esi-2]` against
January's `xor ecx,ecx; mov cx,[esi-2]`, so P1 is the sole prerequisite.
- **Both gates:** all 60 baseline-EXACT rows stay EXACT and the guard passes.
- **Checks:** owner census differs from `build/base` only in `$L` internal labels; `fake_match_scan` reports
0 leads.
### What the programmer meant
If the actor is on foot and an evasion direction exists, the evade vector is built from the requested side (the
same idiom as the evasion routine). Each dive animation's direction is scored by projecting the evade vector onto
the facing vector and its left perpendicular. The best playable animation wins and plays. The debug decision code
is recorded, and a single result is returned.
## `_actor_action_handle_vehicle_entry`
Re-reading January's stream shows that w2's VE3 already has January's semantics:
- the guard exits return the `xor dl,dl` register copy;
- the late exits return a constant `xor al,al`;
- success returns `mov al,1`.
What remains is the layout order of the two duplicated FALSE epilogues (January puts the late-exit copy first) and
one `mov [ebp-4],ecx` emitted after `fmul st(1)` in January.
No w2 law has this fingerprint (A3a is already reproduced; A10/A13/A14/A16/A20 do not apply).
| Shape | Result |
| --- | --- |
| VE7: guards as `goto result_exit` to a label before the single return | byte-identical to VE3 |
| VE8: guards as early `return result;` | 928 bytes, result homed at `[ebp-1]`; reverted |
A `SQUARE`-style parenthesised form has no genuine macro and is inadmissible.
**Reopen:** a source fact that orders the FALSE epilogue copies or the store after the x87 multiply. Do not repeat
V1-V5 or VE1-VE8.
## `_actors_searching_same_position`
The w2 do-not-repeat entry allowed only a genuinely new law.
- **Law checked:** A10 (a default initializer plus conditional reassignment gives a stack home). The fingerprint is
present: base homes result at `[ebp-1]`, January keeps it in BL.
- **SS1:** assigning `result` on every leaf gives 304/14. The stack home disappears as A10 predicts, but VC7
const-splits the result instead of pinning BL, and the actor pointer stays in EBX. Worse; reverted.
- **Residual:** the callee-saved choice diverges before the result initializer (second pursuit location DX/ECX
against CX/EDX).
**Reopen:** authoritative locals or a fact that moves that choice. Do not repeat waveD A-D or SS1.
## Orchestrator proposals
There is no new header text. This wave adds one strict unlock to the pending **P1** packet
(`ai_communication_obj_opus5_150k_w3_20260914.md` proposal 1; actors waveD proposal 2):
1. Change `long animation_impulse` to `short animation_impulse` in both `units.h` prototypes.
2. Match the `units.c` definitions (PROTECTED) and the static helper.
3. In the same commit, unpark `_actor_action_try_to_dive` with the `dive_reopen.c` body and TU enum.
Consumers are the 66 `units.h` includers already listed for P1.
Loaded 100 of 6770 files, more files were not shown because too many files have changed in this diff. Show more