nmarshall commited on
Commit
2e203a0
·
1 Parent(s): aa174d0

Recover entity bounds and inline relink removal gates

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -8,6 +8,8 @@ The [area-query collector](research/AREA_QUERIES.md) adds inclusive-overlap and
8
 
9
  The [world-sector/link collector](research/WORLD_LINKS.md) adds topology, placement and list-order evidence for future converted collision fixtures; original bound producers and full query conformance remain open. Reproduce with `python3 scripts/analyze_world_links.py`.
10
 
 
 
11
  ## Planned tool catalogue
12
 
13
  Names and output formats below are proposals to refine after the relevant research gates. They may become Python scripts, Rust CLI subcommands, or both. Runtime-critical parsers and shared schemas should have one authoritative implementation; orchestration scripts must not diverge from it.
 
8
 
9
  The [world-sector/link collector](research/WORLD_LINKS.md) adds topology, placement and list-order evidence for future converted collision fixtures; original bound producers and full query conformance remain open. Reproduce with `python3 scripts/analyze_world_links.py`.
10
 
11
+ The [entity-bound collector](research/LINK_BOUNDS.md) adds rounded radius/expansion and BSP request fixtures for future collision sidecars; actual leaf/cluster outputs remain open. Reproduce with `python3 scripts/analyze_link_bounds.py`.
12
+
13
  ## Planned tool catalogue
14
 
15
  Names and output formats below are proposals to refine after the relevant research gates. They may become Python scripts, Rust CLI subcommands, or both. Runtime-critical parsers and shared schemas should have one authoritative implementation; orchestration scripts must not diverge from it.
docs/FINDINGS.md CHANGED
@@ -318,3 +318,9 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
318
  **F-089 — Observed placement and list order:** strictly one-sided bounds descend; touching/crossing bounds stay at the parent. Link tails prepend the server record and set owner/linked words; repeated distinct-record insertions reverse insertion order. Full bounds/metadata/relink/query/runtime composition remains open. [LINK-002](research/WORLD_LINKS.md).
319
 
320
  **F-090 — Observed unlink state:** NULL owner skips removal; nonzero owner clears before head/predecessor search. Missing/empty cases select a warning, and removed-record next remains unchanged. Lifecycle/list validity and full runtime/platform behavior remain open. [LINK-003](research/WORLD_LINKS.md).
 
 
 
 
 
 
 
318
  **F-089 — Observed placement and list order:** strictly one-sided bounds descend; touching/crossing bounds stay at the parent. Link tails prepend the server record and set owner/linked words; repeated distinct-record insertions reverse insertion order. Full bounds/metadata/relink/query/runtime composition remains open. [LINK-002](research/WORLD_LINKS.md).
319
 
320
  **F-090 — Observed unlink state:** NULL owner skips removal; nonzero owner clears before head/predecessor search. Missing/empty cases select a warning, and removed-record next remains unchanged. Lifecycle/list validity and full runtime/platform behavior remain open. [LINK-003](research/WORLD_LINKS.md).
321
+
322
+ **F-091 — Observed padded entity bounds:** ordinary bounds add origin/local extents; nonzero model plus tested nonzero orientation uses an origin ± radius cube. Radius clears component sign bits and combines maximum absolute extents; both paths expand by verified 1.0. Full transformed geometry and float/runtime conformance remain open. [BOUNDS-001](research/LINK_BOUNDS.md).
323
+
324
+ **F-092 — Observed leaf-query gates:** metadata resets precede a capacity-128 bound query request. Supplied zero count exits, positive counts request first leaf area, and negative count requests fallback cluster from the extra output. Actual BSP returns and full area/cluster behavior remain unverified. [BOUNDS-002](research/LINK_BOUNDS.md).
325
+
326
+ **F-093 — Observed inline removal precondition:** owned-record fixtures agree with standalone unlink on repairs/clears/stale next/warnings. A NULL-owner inline precheck skips the linked-word clear that standalone unlink performs. Membership/reset/ABI consistency and full relink/runtime behavior remain open. [BOUNDS-003](research/LINK_BOUNDS.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -33,7 +33,7 @@ For implementation items, the minimum done condition is a documented contract, a
33
 
34
  ### First research batch
35
 
36
- Progress: [RE-232–234 / TIME-001–003](research/FRAME_TIMING.md) now cover Linux ELF64 elapsed modification, event sources and pause predicates. The [RE-237–239 follow-up](research/EVENT_QUEUES.md) resolves the pause-field address/bot chain and adds queue/journal evidence. The [RE-241–243 announcement pass](research/AUTH_ANNOUNCEMENTS.md) recovers label tables and recipient paths; The [RE-245–247 cvar pass](research/GAME_CVARS.md) adds all 126 main registration rows and resolves auth_verbosity/callers. The [RE-249–251 change pass](research/CVAR_CHANGES.md) adds cache/notification behavior, four numeric correction branches and direct registration coverage. The [RE-253–255 query pass](research/AUTH_QUERIES.md) adds selector helpers, command-role routing and internal authentication modes. The [RE-257–259 bounds/entry pass](research/SELECTOR_BOUNDS.md) adds integer parsing, selector storage/count evidence and native/QVM/chat route connections. The [RE-261–263 chat pass](research/CHAT_PATHS.md) adds native command metadata/filter evidence and bounded message/recipient paths, including the documented argument-mapping correction. The [RE-265–267 delivery pass](research/CHAT_DELIVERY.md) adds bounded dead-chat predicates, formatter argument routing and echo/fanout calls. The [RE-269–271 expansion pass](research/CHAT_EXPANSION.md) adds marker states, twelve key comparisons and health/momo branches. The [RE-273–275 item/time pass](research/CHAT_ITEMS_TIME.md) adds packed ammunition fields, sampled names and countdown branches. The [RE-277–279 location pass](research/CHAT_LOCATIONS.md) adds leader selection, cache/format boundaries and location-list/spatial evidence. The [RE-281–283 crosshair pass](research/CHAT_CROSSHAIR.md) adds finite geometry fixtures, trace arguments and result routing. The [RE-285–287 native spatial pass](research/NATIVE_SPATIAL.md) confirms Linux trig/spatial services, VM pointer branches and collision/query evidence. The [RE-289–291 collision pass](research/NATIVE_COLLISION.md) adds native candidate filters, result decisions and model/field evidence. The [RE-293–295 composition pass](research/COLLISION_COMPOSITION.md) adds three-lane byte-copy/status agreement and world-result routing. The [RE-297–299 loop pass](research/COLLISION_LOOP.md) adds signed count/status gates, parity scheduling and related sentinel normalization. The [RE-301–303 area pass](research/AREA_QUERIES.md) adds registered address arithmetic, inclusive overlap and count/split ordering boundaries. The [RE-305–307 world-link pass](research/WORLD_LINKS.md) adds bounded sector construction, strict placement/head insertion and unlink state/repair behavior. PROV-002 remains open for full output/reset coverage. PROV-001 remains open for complete scheduling and platform coverage.
37
 
38
  Start with the open questions raised by the current binary evidence. These tasks depend on the linked reports, not on a working rewrite.
39
 
 
33
 
34
  ### First research batch
35
 
36
+ Progress: [RE-232–234 / TIME-001–003](research/FRAME_TIMING.md) now cover Linux ELF64 elapsed modification, event sources and pause predicates. The [RE-237–239 follow-up](research/EVENT_QUEUES.md) resolves the pause-field address/bot chain and adds queue/journal evidence. The [RE-241–243 announcement pass](research/AUTH_ANNOUNCEMENTS.md) recovers label tables and recipient paths; The [RE-245–247 cvar pass](research/GAME_CVARS.md) adds all 126 main registration rows and resolves auth_verbosity/callers. The [RE-249–251 change pass](research/CVAR_CHANGES.md) adds cache/notification behavior, four numeric correction branches and direct registration coverage. The [RE-253–255 query pass](research/AUTH_QUERIES.md) adds selector helpers, command-role routing and internal authentication modes. The [RE-257–259 bounds/entry pass](research/SELECTOR_BOUNDS.md) adds integer parsing, selector storage/count evidence and native/QVM/chat route connections. The [RE-261–263 chat pass](research/CHAT_PATHS.md) adds native command metadata/filter evidence and bounded message/recipient paths, including the documented argument-mapping correction. The [RE-265–267 delivery pass](research/CHAT_DELIVERY.md) adds bounded dead-chat predicates, formatter argument routing and echo/fanout calls. The [RE-269–271 expansion pass](research/CHAT_EXPANSION.md) adds marker states, twelve key comparisons and health/momo branches. The [RE-273–275 item/time pass](research/CHAT_ITEMS_TIME.md) adds packed ammunition fields, sampled names and countdown branches. The [RE-277–279 location pass](research/CHAT_LOCATIONS.md) adds leader selection, cache/format boundaries and location-list/spatial evidence. The [RE-281–283 crosshair pass](research/CHAT_CROSSHAIR.md) adds finite geometry fixtures, trace arguments and result routing. The [RE-285–287 native spatial pass](research/NATIVE_SPATIAL.md) confirms Linux trig/spatial services, VM pointer branches and collision/query evidence. The [RE-289–291 collision pass](research/NATIVE_COLLISION.md) adds native candidate filters, result decisions and model/field evidence. The [RE-293��295 composition pass](research/COLLISION_COMPOSITION.md) adds three-lane byte-copy/status agreement and world-result routing. The [RE-297–299 loop pass](research/COLLISION_LOOP.md) adds signed count/status gates, parity scheduling and related sentinel normalization. The [RE-301–303 area pass](research/AREA_QUERIES.md) adds registered address arithmetic, inclusive overlap and count/split ordering boundaries. The [RE-305–307 world-link pass](research/WORLD_LINKS.md) adds bounded sector construction, strict placement/head insertion and unlink state/repair behavior. The [RE-309–311 bounds pass](research/LINK_BOUNDS.md) adds radius/padding composition, leaf-return gates and inline removal evidence. PROV-002 remains open for full output/reset coverage. PROV-001 remains open for complete scheduling and platform coverage.
37
 
38
  Start with the open questions raised by the current binary evidence. These tasks depend on the linked reports, not on a working rewrite.
39
 
docs/RESEARCH_LEDGER.md CHANGED
@@ -169,3 +169,7 @@ Completed bounded/static RE-301–303 with `analyze_area_queries.py`: ten verifi
169
  ## 2026-10-11 — Sector construction and entity link ownership
170
 
171
  Completed bounded/static RE-305–307 with `analyze_world_links.py`: eight verified graphs, six explicit constructor compositions, 32 local allocation/depth slices, 128 link and 14 unlink slices. LINK-001–003/F-088–090 distinguish normal depth-zero topology from supplied out-of-range counter aliasing, strict parent placement from inclusive overlap, and stale next links from membership. Eight tests check independent topology/list/order/state oracles, paired outputs, float rounding/flag preservation and bounded cycle failures. The byte evaluator adds CMPQ/CLTQ/SHLQ and finite scalar SSE arithmetic/SETBE/JBE. The full suite passes 222 tests; new and prior collision/area reports regenerate byte-identically, and storage/documentation links pass validation. RE-304 remains open; RE-308 tracks bounds/BSP metadata, reset/lifetime/relink ownership, full queries/collision order and original-runtime/platform behavior. No original process or native callee executes.
 
 
 
 
 
169
  ## 2026-10-11 — Sector construction and entity link ownership
170
 
171
  Completed bounded/static RE-305–307 with `analyze_world_links.py`: eight verified graphs, six explicit constructor compositions, 32 local allocation/depth slices, 128 link and 14 unlink slices. LINK-001–003/F-088–090 distinguish normal depth-zero topology from supplied out-of-range counter aliasing, strict parent placement from inclusive overlap, and stale next links from membership. Eight tests check independent topology/list/order/state oracles, paired outputs, float rounding/flag preservation and bounded cycle failures. The byte evaluator adds CMPQ/CLTQ/SHLQ and finite scalar SSE arithmetic/SETBE/JBE. The full suite passes 222 tests; new and prior collision/area reports regenerate byte-identically, and storage/documentation links pass validation. RE-304 remains open; RE-308 tracks bounds/BSP metadata, reset/lifetime/relink ownership, full queries/collision order and original-runtime/platform behavior. No original process or native callee executes.
172
+
173
+ ## 2026-10-11 — Entity bounds, leaf-return gates and inline removal
174
+
175
+ Completed bounded RE-309–311 with `analyze_link_bounds.py`: six verified graphs, file-backed margin/mask constants, 180 finite bounds/radius compositions, twelve supplied leaf-return slices and fourteen inline removals. BOUNDS-001–003/F-091–093 distinguish scalar radius composition from geometry execution, capacity requests from actual return bounds, and owned-removal agreement from the NULL-owner precheck difference. Eight tests check independent rounded bounds, model/angle gates, metadata/request/gate evidence, paired outcomes, removal state and scalar vector/float failure domains. The evaluator adds lower-lane ANDPS/MAXSS/SQRTSS/register MOVAPS and selected 32-bit logical/subtraction operations. The full suite passes 230 tests; new and prior collision/area/tree reports regenerate byte-identically, and storage/documentation link checks pass. RE-308 remains open; RE-312 tracks full metadata/BSP/packed fields, producers/lifetime/relink/float environment and whole runtime/platform behavior. No original process or recorded native callee executes.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -367,3 +367,8 @@ Purpose: turn the supplied installation into an evidence-backed specification of
367
  - [x] RE-306 Recover strict entity placement, parent retention at plane equality and head-insertion/link-word writes ([LINK-002](research/WORLD_LINKS.md)).
368
  - [x] RE-307 Recover standalone unlink owner/flag clearing, head/predecessor repair, missing-list warning and retained-next behavior ([LINK-003](research/WORLD_LINKS.md)).
369
  - [ ] RE-308 Recover complete rotated/padded bounds and BSP leaf/area/cluster metadata, sector/reset/registration and record lifetime invariants, inlined relink equivalence, composed whole-tree query/collision ordering, malformed/exceptional states and original-runtime/all-platform fixtures.
 
 
 
 
 
 
367
  - [x] RE-306 Recover strict entity placement, parent retention at plane equality and head-insertion/link-word writes ([LINK-002](research/WORLD_LINKS.md)).
368
  - [x] RE-307 Recover standalone unlink owner/flag clearing, head/predecessor repair, missing-list warning and retained-next behavior ([LINK-003](research/WORLD_LINKS.md)).
369
  - [ ] RE-308 Recover complete rotated/padded bounds and BSP leaf/area/cluster metadata, sector/reset/registration and record lifetime invariants, inlined relink equivalence, composed whole-tree query/collision ordering, malformed/exceptional states and original-runtime/all-platform fixtures.
370
+
371
+ - [x] RE-309 Recover paired finite model/orientation bounds gates, RadiusFromBounds composition and exact one-unit absolute-bound expansion ([BOUNDS-001](research/LINK_BOUNDS.md)).
372
+ - [x] RE-310 Recover metadata reset writes, CM_BoxLeafnums request arguments and isolated zero/positive/negative return classification ([BOUNDS-002](research/LINK_BOUNDS.md)).
373
+ - [x] RE-311 Recover inline owned-removal agreement and NULL-owner precheck difference against standalone unlink fixtures ([BOUNDS-003](research/LINK_BOUNDS.md)).
374
+ - [ ] RE-312 Recover full area/cluster accumulation and overflow/fallback/packed-field behavior, actual BSP output bounds, object/angle/layout producers, membership/reset/relink/ABI invariants, exceptional float environment and whole-query/collision/original-runtime/all-platform fixtures.
docs/research/LINK_BOUNDS.md ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Entity bounds, inline removal and leaf-return gates
2
+
3
+ Static/bounded pass, 2026-10-11. Reproduce with `python3 scripts/analyze_link_bounds.py`; check with `python3 scripts/test_link_bounds.py`. [Generated evidence](link-bounds.json) verifies original/disassembly identities for both Linux ELF64 engines and records **180 finite bound/radius compositions, twelve supplied leaf-return slices and fourteen inline-removal compositions**. It retains SV_LinkEntity, SV_UnlinkEntity and RadiusFromBounds graphs, plus file-backed constants. This extends [placement and unlink evidence](WORLD_LINKS.md).
4
+
5
+ No original process, entity lookup, BSP query, formatting call or full linking pipeline executes. RadiusFromBounds is evaluated separately as a bounded instruction slice; its result is explicitly injected after the stopped caller's radius call. Inline removal similarly stops lookup calls and supplies the same fixture record. Finite arithmetic fixtures do not establish original SSE/MXCSR, exceptional-float or other-platform behavior.
6
+
7
+ ## BOUNDS-001: model/angle gate, radius and one-unit expansion
8
+
9
+ Starting at the observed model word read, the caller uses object +0x1b0 to choose whether to examine orientation words +0x1f4/+0x1f8/+0x1fc. Model zero skips orientation checks. With nonzero model, any tested nonzero orientation component requests RadiusFromBounds; all three components equal zero select the ordinary path. Negative zero compares equal to positive zero. The word is a nonzero predicate, not an exact-one predicate.
10
+
11
+ Ordinary bounds are:
12
+
13
+ ```
14
+ absolute_min[i] = origin[i] + local_min[i]
15
+ absolute_max[i] = origin[i] + local_max[i]
16
+ ```
17
+
18
+ Origin is object +0x1e8, local min +0x1b4, local max +0x1c0, and absolute min/max +0x1d0/+0x1dc. The rotated-model path builds a cube `origin[i] ± radius` without using the actual nonzero angle magnitude inside this slice.
19
+
20
+ RadiusFromBounds loads mask **0x7fffffff** and ANDs each local component to clear its sign bit, takes the larger absolute min/max component on each axis, squares them, adds the squares, and requests scalar square root. The mask's bits are not a numeric float constant (interpreting it as a float would be NaN). The bounded helper executes its observed lower-lane operations through SQRTSS and stops before return.
21
+
22
+ Both caller paths then subtract **1.0** from all minimum components and add **1.0** to all maximum components. The margin is verified from original file bytes: **0x3f800000**. Arithmetic rounds through binary32 after each instruction, including radius and origin/padding composition.
23
+
24
+ Each engine has ninety compositions: three local bound pairs, two origins, model words 0/1/4, and five orientation patterns (zero, negative zero, or 90 in each one of the three fields). Bounds include a 3/4/0 radius-five case, asymmetric bounds giving an inexact square root, and a zero-radius point. Independent fixtures check final rounded values and path selection. Actual transformed collision geometry, coordinate/angle conventions, runtime NaNs/denormals/rounding controls and full extent validity remain open.
25
+
26
+ ## BOUNDS-002: metadata reset, leaf request and return classification
27
+
28
+ After padding, the caller zeroes server-record words +0xe0 and +0x124 and sets +0x128/+0x12c to 0xffffffff. Their complete layouts/ownership remain open; downstream retained code associates them with cluster/area metadata.
29
+
30
+ The stopped CM_BoxLeafnums request has:
31
+
32
+ | Argument register | Observed value |
33
+ | --- | --- |
34
+ | rdi | Absolute-min pointer, object +0x1d0 |
35
+ | rsi | Absolute-max pointer, object +0x1dc |
36
+ | rdx | Output buffer, stack +0x10 |
37
+ | ecx | Capacity 128 |
38
+ | r8 | Extra output pointer, stack +0x21c |
39
+
40
+ The extra output's complete type and producer behavior are not established by this request alone. A separately retained file-backed **32.0** constant belongs to an unexecuted packed-field path elsewhere in SV_LinkEntity; it is not the expansion margin.
41
+
42
+ The isolated post-query slices supply EAX counts -1, 0, 1, 127, 128 and 129, first buffer word 17, and extra output word 29. Each count is saved at stack +12 before branching:
43
+
44
+ - Count zero reaches frame cleanup before the placement/link-count increment.
45
+ - Signed negative count clears server-record +0xe0 and requests CM_LeafCluster with the supplied extra word 29. Retained code stores its return at +0x124 before placement; that callee/return is not executed here.
46
+ - Positive count requests the first CM_LeafArea with supplied buffer word 17. The sampled 129 still follows this path: there is no additional capacity check in this immediate gate. It is an intentionally out-of-contract fixture, not an observed BSP query return.
47
+
48
+ Area/cluster accumulation, 16-cluster overflow/fallback handling, real output counts and reset/link failure state remain open. These slices do not establish what CM_BoxLeafnums returns for any actual map or malformed box.
49
+
50
+ ## BOUNDS-003: inline removal versus standalone unlink state
51
+
52
+ The first supplied SV_SvEntityForGentity return is checked for a nonzero record owner pointer. NULL owner goes directly to metadata work, skipping the second lookup and removal body. Nonzero owner requests a second lookup; the composition explicitly supplies the same record at its return, then evaluates inline removal until metadata work or a stopped warning request.
53
+
54
+ Seven cases per engine match the prior standalone fixtures: NULL owner, head, middle, tail, sole member, missing record and empty list. For the six owned cases, inline removal agrees with standalone unlink on final list order, owner clearing, linked-word clearing, retained next pointer and warning selection. Missing/empty cases request Com_Printf; no warning callee executes.
55
+
56
+ There is a precise precondition difference: standalone unlink clears game object +0x1a0 even for NULL owner, whereas the inline NULL-owner precheck skips that clear. The deliberately inconsistent fixture begins with linked word one and owner NULL; inline leaves one and standalone clears it. This is a supplied raw state, not proof that normal linking reaches inconsistent membership. Full caller/registration/reset invariants and complete relink execution remain open.
57
+
58
+ ## Evaluation scope and remaining work
59
+
60
+ The scalar byte evaluator adds low-lane ANDPS/MAXSS/SQRTSS, register-only low-lane MOVAPS and selected 32-bit subtraction/AND. It does not model complete 128-bit vector registers, vector-memory MOVAPS or the original floating-point environment. MAXSS source selection is retained on finite ties (including signed zero); nonfinite arithmetic and negative square root fail closed. Tests also check flag preservation, unknown registers and unsupported vector domains.
61
+
62
+ Eight tests cover independent finite bounds/radius oracles, raw model/signed-zero gates, margin/metadata/request identity, leaf-return classification, paired engines, owned-removal agreement and NULL-owner difference, and evaluator failure boundaries. Earlier collision/area/tree reports regenerate unchanged.
63
+
64
+ For Rust, keep absolute-bound preparation separate from narrow-phase transforms, specify expansion and rounding fixtures, distinguish membership preconditions from pointer presence, and treat BSP outputs as an explicit bounded contract. RE-312 tracks full leaf/area/cluster and packed-field behavior, real query outputs, object/angle/layout producers, relink/reset/ABI invariants, exceptional floats and whole-query/collision/original-runtime/platform conformance. Broad collision and port tasks remain open.
docs/research/README.md CHANGED
@@ -109,3 +109,5 @@ The extension collector adds five focused tests for native tail calls, command-r
109
  [Entity addressing and area-query boundaries](AREA_QUERIES.md) adds RE-301–303: registered low-width addressing, inclusive overlap/capacity insertion and static list/strict split ordering. Reproduce with `python3 scripts/analyze_area_queries.py`; check with `python3 scripts/test_area_queries.py`. Complete node/list/count/ABI ownership, query composition and runtime/platform conformance remain open.
110
 
111
  [World-sector construction and entity linking](WORLD_LINKS.md) adds RE-305–307: bounded sector topology/allocation, strict placement/head insertion and standalone unlink repairs/state. Reproduce with `python3 scripts/analyze_world_links.py`; check with `python3 scripts/test_world_links.py`. Complete bounds/BSP metadata, reset/relink/list ownership and whole-query/runtime/platform conformance remain open.
 
 
 
109
  [Entity addressing and area-query boundaries](AREA_QUERIES.md) adds RE-301–303: registered low-width addressing, inclusive overlap/capacity insertion and static list/strict split ordering. Reproduce with `python3 scripts/analyze_area_queries.py`; check with `python3 scripts/test_area_queries.py`. Complete node/list/count/ABI ownership, query composition and runtime/platform conformance remain open.
110
 
111
  [World-sector construction and entity linking](WORLD_LINKS.md) adds RE-305–307: bounded sector topology/allocation, strict placement/head insertion and standalone unlink repairs/state. Reproduce with `python3 scripts/analyze_world_links.py`; check with `python3 scripts/test_world_links.py`. Complete bounds/BSP metadata, reset/relink/list ownership and whole-query/runtime/platform conformance remain open.
112
+
113
+ [Entity bounds, inline removal and leaf-return gates](LINK_BOUNDS.md) adds RE-309–311: finite radius/padding compositions, metadata/query/return gates and owned-removal agreement with a NULL-owner precheck distinction. Reproduce with `python3 scripts/analyze_link_bounds.py`; check with `python3 scripts/test_link_bounds.py`. Full BSP metadata, producer/lifetime/relink/float and runtime/platform contracts remain open.
docs/research/link-bounds.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_collision_composition.py CHANGED
@@ -3,6 +3,7 @@
3
  import gzip
4
  import itertools
5
  import json
 
6
  from pathlib import Path
7
  import re
8
  from analyze_chat_crosshair import bits,number
@@ -62,6 +63,10 @@ def evaluate(graph,start,stops,memory,registers,limit=128,capture_registers=Fals
62
  if op=='nop':pass
63
  elif op in ['movl','movq','movss','movabsq']:
64
  a,b=args.split(', ');width=8 if op in ['movq','movabsq'] else 4;write(b,read(a,width),width)
 
 
 
 
65
  elif op=='cltq':
66
  v=read_reg('%eax');write('%rax',v if v<2**31 else v-2**32,8)
67
  elif op=='movslq':
@@ -89,16 +94,23 @@ def evaluate(graph,start,stops,memory,registers,limit=128,capture_registers=Fals
89
  elif op=='sarq':
90
  a,b=args.split(', ');v=read(b,8);v=v if v<2**63 else v-2**64
91
  write(b,v>>(read(a,8)&63),8);equal=None;signed_le=None;ge=greater=below=None
92
- elif op in ['xorl','orl','addl','addq','subq']:
93
  width=8 if op in ['addq','subq'] else 4
94
  a,b=args.split(', ');v,w=read(a,width),read(b,width)
95
- result=v^w if op=='xorl' else v|w if op=='orl' else w-v if op=='subq' else v+w
96
  write(b,result,width);equal=(result&((1<<(width*8))-1))==0
97
  signed_le=None;ge=greater=below=None
98
  elif op=='xorps':
99
  a,b=args.split(', ')
100
  if a!=b:raise ValueError('unsupported SIMD xor')
101
  write(b,0,4) # Does not change integer flags.
 
 
 
 
 
 
 
102
  elif op in ['addss','subss','mulss']:
103
  a,b=args.split(', ');v,w=number(read(a,4)),number(read(b,4))
104
  write(b,bits(w+v if op=='addss' else w-v if op=='subss' else w*v),4)
 
3
  import gzip
4
  import itertools
5
  import json
6
+ import math
7
  from pathlib import Path
8
  import re
9
  from analyze_chat_crosshair import bits,number
 
63
  if op=='nop':pass
64
  elif op in ['movl','movq','movss','movabsq']:
65
  a,b=args.split(', ');width=8 if op in ['movq','movabsq'] else 4;write(b,read(a,width),width)
66
+ elif op=='movaps':
67
+ a,b=args.split(', ')
68
+ if not a.startswith('%xmm') or not b.startswith('%xmm'):raise ValueError('unsupported vector move')
69
+ write(b,read(a,4),4) # Scalar low lane only.
70
  elif op=='cltq':
71
  v=read_reg('%eax');write('%rax',v if v<2**31 else v-2**32,8)
72
  elif op=='movslq':
 
94
  elif op=='sarq':
95
  a,b=args.split(', ');v=read(b,8);v=v if v<2**63 else v-2**64
96
  write(b,v>>(read(a,8)&63),8);equal=None;signed_le=None;ge=greater=below=None
97
+ elif op in ['xorl','orl','andl','subl','addl','addq','subq']:
98
  width=8 if op in ['addq','subq'] else 4
99
  a,b=args.split(', ');v,w=read(a,width),read(b,width)
100
+ result=v^w if op=='xorl' else v|w if op=='orl' else v&w if op=='andl' else w-v if op in ['subl','subq'] else v+w
101
  write(b,result,width);equal=(result&((1<<(width*8))-1))==0
102
  signed_le=None;ge=greater=below=None
103
  elif op=='xorps':
104
  a,b=args.split(', ')
105
  if a!=b:raise ValueError('unsupported SIMD xor')
106
  write(b,0,4) # Does not change integer flags.
107
+ elif op=='andps':
108
+ a,b=args.split(', ');write(b,read(a,4)&read(b,4),4)
109
+ elif op=='maxss':
110
+ a,b=args.split(', ');v,w=number(read(a,4)),number(read(b,4))
111
+ write(b,read(b,4) if w>v else read(a,4),4)
112
+ elif op=='sqrtss':
113
+ a,b=args.split(', ');write(b,bits(math.sqrt(number(read(a,4)))),4)
114
  elif op in ['addss','subss','mulss']:
115
  a,b=args.split(', ');v,w=number(read(a,4)),number(read(b,4))
116
  write(b,bits(w+v if op=='addss' else w-v if op=='subss' else w*v),4)
scripts/analyze_link_bounds.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Bounded finite bounds/radius composition, inline removal and leaf-return gates."""
3
+ import gzip
4
+ import itertools
5
+ import json
6
+ from pathlib import Path
7
+ import re
8
+ from analyze_area_queries import site,run
9
+ from analyze_chat_crosshair import bits,number
10
+ from analyze_collision_composition import get_word,put_word
11
+ from analyze_engine_control import control_flow
12
+ from analyze_frame_timing import read_address
13
+ from analyze_native import elf
14
+ from analyze_qvm import write_json
15
+ from analyze_vm_extensions import native_functions,verified
16
+ from analyze_world_links import vector
17
+
18
+
19
+ def main():
20
+ out={'native':{},'limitations':'Synthetic finite lower-32-bit SSE/integer slices with supplied object '
21
+ 'fields, server records and native return values. Radius helper is evaluated separately and its '
22
+ 'result explicitly injected at the caller return boundary; no original process/native call/full '
23
+ 'link pipeline executes. ABI/allocation/lifecycle, BSP metadata, exceptional floats/MXCSR and '
24
+ 'original-runtime/all-platform conformance remain open.'}
25
+ obj,record,stack=0x10000,0x20000,0x40000
26
+ for label in ['linux--Quake3-UrT-Ded.x86_64','linux--Quake3-UrT.x86_64']:
27
+ r=json.loads(Path(f'docs/research/native/{label}.json').read_text());data=verified((Path('.cache/UrbanTerror43')/r['source']['path']).read_bytes(),r['source']['sha256'])
28
+ a=r['artifacts']['disassembly.txt'];fs=native_functions(verified(gzip.open(a['path'],'rb').read(),a['uncompressed_sha256']).decode())
29
+ graphs={n:control_flow(fs[n]) for n in ['SV_LinkEntity','SV_UnlinkEntity','RadiusFromBounds']};g=graphs['SV_LinkEntity'];rows=g['instructions'];metadata,_=elf(data)
30
+ constants=[];constant_memory={}
31
+ for name,graph in graphs.items():
32
+ for x in graph['instructions']:
33
+ if x['opcode']=='movss' and '(%rip)' in x['operands']:
34
+ address=int(re.search(r'#\s*0x([0-9a-f]+)',x['operands'])[1],16);raw,off=read_address(data,metadata['sections'],address,4)
35
+ value=int.from_bytes(raw,'little');put_word(constant_memory,address,value)
36
+ constants.append({'function':name,'instruction':x['address'],'address':address,'file_offset':off,'bits':value})
37
+ def call_sites(name):return [i for i,x in enumerate(rows) if x['opcode']=='callq' and '<'+name+'>' in x['operands']]
38
+ radius_calls=call_sites('RadiusFromBounds');leaf_calls=call_sites('CM_BoxLeafnums')
39
+ if len(radius_calls)!=1 or len(leaf_calls)!=1:raise ValueError('bound call count changed')
40
+ radius_call=rows[radius_calls[0]]['address'];after_radius=rows[radius_calls[0]+1]['address'];leaf_call=rows[leaf_calls[0]]['address'];after_leaf=rows[leaf_calls[0]+1]['address']
41
+ start=site(g,'movl','0x1b0(%rbx), %esi');cleanup=site(g,'addq','$0x228, %rsp')
42
+ bounds=[];fixtures=[([-3,-4,0],[3,4,0]),([-1,-2,-2],[2,1,1]),([0,0,0],[0,0,0])]
43
+ for (low,high),origin,model,angles in itertools.product(fixtures,[[0,0,0],[10,-20,30]],[0,1,4],[[0,0,0],[-0.0,0,0],[90,0,0],[0,90,0],[0,0,90]]):
44
+ m=dict(constant_memory);put_word(m,obj+0x1b0,model)
45
+ for base,v in [(0x1b4,low),(0x1c0,high),(0x1e8,origin),(0x1f4,angles)]:
46
+ for axis,value in enumerate(v):put_word(m,obj+base+4*axis,bits(value))
47
+ t=run(g,start,[radius_call,leaf_call],m,{'%rbx':obj,'%r12':record,'%rsp':stack});trace=list(t['trace']);radius=None;radius_trace=[]
48
+ if t['stop']==radius_call:
49
+ rg=graphs['RadiusFromBounds'];end=next(x['address'] for x in rg['instructions'] if x['opcode']=='retq')
50
+ rt=run(rg,rg['entry'],[end],t['memory'],{'%rdi':t['registers']['rdi'],'%rsi':t['registers']['rsi']})
51
+ radius=number(rt['registers']['xmm0']);radius_trace=rt['trace'];regs=t['registers'];regs['xmm0']=rt['registers']['xmm0']
52
+ t=run(g,after_radius,[leaf_call],t['memory'],regs);trace+=t['trace']
53
+ regs=t['registers'];m=t['memory']
54
+ bounds.append({'local_min':low,'local_max':high,'origin':origin,'model_word':model,'angles':angles,
55
+ 'radius_path':radius is not None,'radius':radius,'radius_trace':radius_trace,'trace':trace,
56
+ 'absolute_min':vector(m,obj+0x1d0),'absolute_max':vector(m,obj+0x1dc),
57
+ 'metadata_words':{str(offset):get_word(m,record+offset) for offset in [0xe0,0x124,0x128,0x12c]},
58
+ 'leaf_request':{'min_pointer':regs['rdi'],'max_pointer':regs['rsi'],'output_pointer':regs['rdx'],
59
+ 'capacity':regs['rcx'],'extra_pointer':regs['r8']},'writes':t['writes']})
60
+ # Leaf count is an isolated supplied callee return, with first leaf/top-node words supplied.
61
+ first_area=rows[call_sites('CM_LeafArea')[0]]['address'];leaf_cluster_sites=call_sites('CM_LeafCluster');negative_cluster=rows[leaf_cluster_sites[-1]]['address']
62
+ leaf_returns=[]
63
+ for count in [-1,0,1,127,128,129]:
64
+ m={};put_word(m,stack+0x10,17);put_word(m,stack+0x21c,29)
65
+ t=run(g,after_leaf,[cleanup,first_area,negative_cluster],m,{'%rax':count,'%rsp':stack,'%r12':record})
66
+ leaf_returns.append({'supplied_count':count,'path':'cleanup' if t['stop']==cleanup else 'first_area' if t['stop']==first_area else 'fallback_cluster',
67
+ 'saved_count_bits':get_word(t['memory'],stack+12),'requested_leaf':t['registers'].get('rdi'),
68
+ 'trace':t['trace'],'writes':t['writes']})
69
+ # Inline removal begins after first lookup. Owned records request a second lookup;
70
+ # stop it and inject the same supplied record before evaluating the removal body.
71
+ lookup=call_sites('SV_SvEntityForGentity')
72
+ if len(lookup)!=2:raise ValueError('lookup prelude changed')
73
+ prelude_start=rows[lookup[0]+1]['address'];second_lookup=rows[lookup[1]]['address'];after_second_lookup=rows[lookup[1]+1]['address']
74
+ metadata_start=site(g,'movl','0x1b0(%rbx), %ecx');warning_call=next(x['address'] for x in rows if x['opcode']=='callq' and '<Com_Printf>' in x['operands'])
75
+ node=0x60000;records=[0x70000+i*312 for i in range(4)];inline=[]
76
+ for name,order,target,owner in [('no_owner',[0,1,2],1,0),('head',[0,1,2],0,node),('middle',[0,1,2],1,node),('tail',[0,1,2],2,node),('only',[0],0,node),('missing',[0,1,2],3,node),('empty',[],3,node)]:
77
+ m={};put_word(m,obj+0x1a0,1);put_word(m,node+24,records[order[0]] if order else 0,8)
78
+ for i,p in enumerate(records):put_word(m,p,node,8);put_word(m,p+8,records[i+1] if i<3 else 0,8)
79
+ if order:put_word(m,records[order[-1]]+8,0,8)
80
+ put_word(m,records[target],owner,8)
81
+ t=run(g,prelude_start,[second_lookup,metadata_start],m,{'%rbx':obj,'%rax':records[target]});trace=list(t['trace']);requested=t['stop']==second_lookup
82
+ if requested:
83
+ regs=t['registers'];regs['rax']=records[target]
84
+ t=run(g,after_second_lookup,[metadata_start,warning_call],t['memory'],regs);trace+=t['trace']
85
+ cursor=get_word(t['memory'],node+24,8);remaining=[]
86
+ while cursor:
87
+ if cursor in remaining or len(remaining)>4:raise ValueError('inline fixture cycle')
88
+ remaining.append(cursor);cursor=get_word(t['memory'],cursor+8,8)
89
+ inline.append({'case':name,'target_index':target,'second_lookup_requested':requested,'warning_requested':t['stop']==warning_call,
90
+ 'remaining_indices':[records.index(p) for p in remaining],'owner_after':get_word(t['memory'],records[target],8),
91
+ 'next_after':get_word(t['memory'],records[target]+8,8),'linked_word':get_word(t['memory'],obj+0x1a0),'trace':trace})
92
+ out['native'][label]={'source':r['source'],'disassembly_sha256':a['uncompressed_sha256'],'graphs':graphs,
93
+ 'constants':constants,'sites':{'bounds_start':start,'radius_call':radius_call,'after_radius_call':after_radius,'leaf_call':leaf_call,
94
+ 'after_leaf_call':after_leaf,'first_area_call':first_area,'fallback_cluster_call':negative_cluster,'cleanup':cleanup,
95
+ 'inline_start':prelude_start,'second_lookup':second_lookup,'metadata_start':metadata_start,'warning_call':warning_call},
96
+ 'bounds_compositions':bounds,'leaf_return_traces':leaf_returns,'inline_removal_compositions':inline}
97
+ write_json(Path('docs/research/link-bounds.json'),out)
98
+ print('Recovered 180 finite bound/radius, 12 leaf-return and 14 inline-removal compositions')
99
+
100
+
101
+ if __name__=='__main__':main()
scripts/test_link_bounds.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Independent bounds/radius, relink-state and leaf-return boundary oracles."""
3
+ import json
4
+ import math
5
+ import struct
6
+ from pathlib import Path
7
+ import unittest
8
+ from analyze_chat_crosshair import bits,number
9
+ from analyze_collision_composition import evaluate
10
+ from test_collision_composition import graph
11
+
12
+
13
+ def f32(value):return struct.unpack('<f',struct.pack('<f',value))[0]
14
+
15
+
16
+ class LinkBoundsTests(unittest.TestCase):
17
+ @classmethod
18
+ def setUpClass(cls):
19
+ cls.r=json.loads(Path('docs/research/link-bounds.json').read_text())
20
+ cls.prior=json.loads(Path('docs/research/world-links.json').read_text())
21
+
22
+ def test_bounds_rotation_gate_and_independent_radius_oracle(self):
23
+ for n in self.r['native'].values():
24
+ self.assertEqual(len(n['bounds_compositions']),90)
25
+ for x in n['bounds_compositions']:
26
+ rotated=bool(x['model_word'] and any(a!=0 for a in x['angles']))
27
+ self.assertEqual(x['radius_path'],rotated)
28
+ if rotated:
29
+ radius=f32(math.sqrt(sum(max(abs(lo),abs(hi))**2 for lo,hi in zip(x['local_min'],x['local_max']))))
30
+ self.assertEqual(x['radius'],radius)
31
+ low=[f32(f32(o-radius)-1) for o in x['origin']];high=[f32(f32(o+radius)+1) for o in x['origin']]
32
+ helper={row['address']:row for row in n['graphs']['RadiusFromBounds']['instructions']}
33
+ self.assertEqual(helper[x['radius_trace'][-1]]['opcode'],'sqrtss')
34
+ self.assertTrue(all(not helper[pc]['opcode'].startswith('call') for pc in x['radius_trace']))
35
+ else:
36
+ low=[o+b-1 for o,b in zip(x['origin'],x['local_min'])];high=[o+b+1 for o,b in zip(x['origin'],x['local_max'])]
37
+ self.assertEqual(x['radius_trace'],[])
38
+ self.assertEqual(x['absolute_min'],low);self.assertEqual(x['absolute_max'],high)
39
+
40
+ def test_padding_constant_metadata_reset_and_leaf_arguments(self):
41
+ for n in self.r['native'].values():
42
+ g=n['graphs']['SV_LinkEntity'];pad=next(x['address'] for x in g['instructions'] if x['opcode']=='movss' and x['operands'].split('#')[0].strip().endswith(', %xmm14'))
43
+ self.assertEqual(next(c['bits'] for c in n['constants'] if c['instruction']==pad),0x3f800000)
44
+ for x in n['bounds_compositions']:
45
+ self.assertEqual(x['metadata_words'],{'224':0,'292':0,'296':0xffffffff,'300':0xffffffff})
46
+ self.assertEqual(x['leaf_request'],{'min_pointer':0x101d0,'max_pointer':0x101dc,'output_pointer':0x40010,'capacity':128,'extra_pointer':0x4021c})
47
+ self.assertIn(pad,x['trace'])
48
+
49
+ def test_zero_angles_signed_zero_and_model_word_not_exact_one(self):
50
+ for n in self.r['native'].values():
51
+ cases=n['bounds_compositions']
52
+ self.assertTrue(all(not x['radius_path'] for x in cases if not x['model_word'] or all(a==0 for a in x['angles'])))
53
+ self.assertTrue(all(x['radius_path'] for x in cases if x['model_word']==4 and any(a!=0 for a in x['angles'])))
54
+ self.assertTrue(any(math.copysign(1,x['angles'][0])<0 for x in cases))
55
+
56
+ def test_leaf_count_return_gates_and_supplied_argument_distinction(self):
57
+ for n in self.r['native'].values():
58
+ self.assertEqual(len(n['leaf_return_traces']),6)
59
+ for x in n['leaf_return_traces']:
60
+ count=x['supplied_count'];expected='cleanup' if count==0 else 'fallback_cluster' if count<0 else 'first_area'
61
+ self.assertEqual(x['path'],expected);self.assertEqual(x['saved_count_bits'],count&0xffffffff)
62
+ self.assertEqual(x['requested_leaf'],None if count==0 else 29 if count<0 else 17)
63
+ self.assertEqual(x['writes'],[{'address':0x4000c,'bytes':4}]+([{'address':0x200e0,'bytes':4}] if count<0 else []))
64
+
65
+ def test_inline_owned_removal_agrees_with_standalone_but_null_owner_skips(self):
66
+ for label,n in self.r['native'].items():
67
+ prior={x['case']:x for x in self.prior['native'][label]['unlink_traces']}
68
+ self.assertEqual(len(n['inline_removal_compositions']),7)
69
+ for x in n['inline_removal_compositions']:
70
+ p=prior[x['case']]
71
+ for field in ['remaining_indices','owner_after','next_after']:self.assertEqual(x[field],p[field])
72
+ owned=x['case']!='no_owner'
73
+ self.assertEqual(x['second_lookup_requested'],owned)
74
+ self.assertEqual(x['linked_word'],0 if owned else 1)
75
+ self.assertEqual(x['warning_requested'],p['warning_path'])
76
+ if owned:self.assertEqual(x['linked_word'],p['linked_word'])
77
+
78
+ def test_paired_engine_results_match(self):
79
+ a,b=self.r['native'].values()
80
+ for key in ['bounds_compositions','leaf_return_traces','inline_removal_compositions']:
81
+ def outputs(n):return [{k:v for k,v in x.items() if k not in ['trace','radius_trace','writes']} for x in n[key]]
82
+ self.assertEqual(outputs(a),outputs(b))
83
+
84
+ def test_scalar_sse_source_ties_masks_sqrt_and_flag_preservation(self):
85
+ g=graph(('cmpl','$0x1, %edi'),('andps','%xmm0, %xmm1'),('maxss','%xmm2, %xmm1'),('sqrtss','%xmm1, %xmm3'),('movaps','%xmm3, %xmm4'),('sete','%al'))
86
+ t=evaluate(g,1,[7],{}, {'%rdi':1,'%rax':0,'%xmm0':0x7fffffff,'%xmm1':bits(-9),'%xmm2':bits(4)},capture_registers=True)
87
+ self.assertEqual(number(t['registers']['xmm4']),3);self.assertEqual(t['registers']['rax'],1)
88
+ g=graph(('maxss','%xmm0, %xmm1'))
89
+ t=evaluate(g,1,[2],{}, {'%xmm0':0x80000000,'%xmm1':0},capture_registers=True)
90
+ self.assertEqual(t['registers']['xmm1'],0x80000000)
91
+ g=graph(('subl','$0x1, %eax'),('andl','$0x3, %eax'))
92
+ t=evaluate(g,1,[3],{}, {'%rax':0},capture_registers=True)
93
+ self.assertEqual(t['registers']['rax'],3)
94
+
95
+ def test_unknown_vector_domain_negative_sqrt_and_nonfinite_rejected(self):
96
+ with self.assertRaises(ValueError):evaluate(graph(('movaps','(%rbx), %xmm0')),1,[2],{}, {'%rbx':0})
97
+ with self.assertRaises(ValueError):evaluate(graph(('sqrtss','%xmm0, %xmm1')),1,[2],{}, {'%xmm0':bits(-1)})
98
+ with self.assertRaises(ValueError):evaluate(graph(('maxss','%xmm0, %xmm1')),1,[2],{}, {'%xmm0':0x7fc00000,'%xmm1':0})
99
+ with self.assertRaises(ValueError):evaluate(graph(('andps','%xmm0, %xmm1')),1,[2],{}, {'%xmm0':0})
100
+
101
+
102
+ if __name__=='__main__':unittest.main()