Capture current mission, AI, map and administration development

Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
2026-10-13 22:52:53 +02:00
parent a951a96edc
commit 6a667d8deb
8104 changed files with 2124945 additions and 2061 deletions
@@ -0,0 +1,19 @@
check: /tmp/err-policy-xvfc14oj/check.cpp:266: int main(): Assertion `f.group.additions==1&&f.unit.Waypoint->radius==radius&&f.unit.Waypoint->configured==1' failed.
Traceback (most recent call last):
File "/tmp/erri-search-radius-1611/tests/test_hq_delivered_task.py", line 34, in <module>
run(r'''
~~~^^^^^
#include <cassert>
^^^^^^^^^^^^^^^^^^
...<199 lines>...
}
^
''')
^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/err-policy-xvfc14oj/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,15 @@
{
"scope": "Ordinary ground memory SEARCH only: snapshot finite delivered-belief uncertainty clamped45..110m at Assign/Retask, validate copied radius before spawn, set once after successful spawn and before old assignment release/new installation. No live read of player location, new actor, settings, radius animation or deadline extension.",
"exclusions": [
"Missing-patrol investigation and road/site-security Defend tasks retain existing defaults.",
"INTERCEPT/CONTAIN and air direct-task path remain unchanged.",
"WORKBENCH NativePatrolHQProbe now initializes the same snapshot for its direct ordinary pending-task fixture; no native run."
],
"proof": "Six focused programs green; old HQ actual DeliverOrder compiles and runtime-fails radius assertion. Omitted finite/range admission and omitted Retask snapshot each compile/runtime-fail. Existing actual40m/30s negative-sweep witness remains byte-identical and tested separately; larger native search grid does not prove larger cleared area.",
"validation_limits": [
"Native AI/waypoint/group boundaries are doubles; parser/radius policy and actual HQ delivery/queue expressions run as C++.",
"Grid grows with radius;110m is a size bound, not a measured performance bound or FPS improvement.",
"Native compilation, actual search movement and multiplayer remain unqualified.",
"Historical prepare.py/adapt-tests.py were construction scratch; only manifest-listed files are authoritative."
]
}
@@ -0,0 +1,230 @@
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c
@@ -1085,12 +1085,17 @@
if (task.RoadIntercept && !ERR_HQRoadIntercept.Eligible(belief, now)) { LostOrder(unit, now); return; }
}
if (Fighting(unit)) { LostOrder(unit, now); return; }
+ bool memorySearch = task.Role == ERR_HQPursuitRole.SEARCH && !task.Investigation()
+ && !task.RoadIntercept && task.SecuritySite.IsEmpty();
+ if (memorySearch && (!ERR_HQPursuitRules.Finite(task.SearchRadiusMetres)
+ || task.SearchRadiusMetres < 45 || task.SearchRadiusMetres > 110)) { LostOrder(unit, now); return; }
int deliveryId;
if (unit.Downlink) deliveryId = unit.Downlink.MessageId;
ResourceName waypointResource = ERR_Resources.SearchWaypoint();
if (task.RoadIntercept || !task.SecuritySite.IsEmpty()) waypointResource = ERR_Resources.DefendWaypoint();
AIWaypoint waypoint = AIWaypoint.Cast(ERR_Actors.Spawn(waypointResource, ERR_Actors.Ground(task.Destination)));
if (!waypoint) { LostOrder(unit, now); m_Journal.Write("ORDER FAILED", "waypoint spawn failed", order.MessageId); return; }
+ if (memorySearch) waypoint.SetCompletionRadius(task.SearchRadiusMetres);
if (unit.Assignment) RememberCompletedHunt(unit.Task, now);
unit.Release();
unit.Task = task;
@@ -1294,6 +1299,7 @@
if (!m_Coverage.Select(belief.PredictedPosition, belief.Radius, belief.Position, now, destination, reserved)) continue;
ERR_HQTask task = new ERR_HQTask();
task.BeliefId = belief.BeliefId; task.BeliefRevision = belief.BeliefRevision; task.Role = ERR_HQPursuitRole.SEARCH;
+ task.SearchRadiusMetres = ERR_HQPursuitRules.SearchRadius(belief.Radius);
task.Destination = destination; task.QueuedAt = now;
task.ExpiresAt = now + ERR_HQPursuitRules.TaskLifetimeSeconds(task.Role, vector.DistanceXZ(unit.PlanPosition, destination)) * 1000;
task.ApproachBearing = bearing;
@@ -1544,6 +1550,7 @@
if (!best) continue;
ERR_HQTask task = new ERR_HQTask();
task.BeliefId = belief.BeliefId; task.BeliefRevision = belief.BeliefRevision; task.Role = role; task.RoadIntercept = roadIntercept;
+ if (role == ERR_HQPursuitRole.SEARCH && !roadIntercept) task.SearchRadiusMetres = ERR_HQPursuitRules.SearchRadius(belief.Radius);
task.Destination = destination; task.QueuedAt = now;
task.ExpiresAt = now + ERR_HQPursuitRules.TaskLifetimeSeconds(role, vector.DistanceXZ(best.PlanPosition, destination)) * 1000;
task.ApproachBearing = bestBearing;
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_HQPursuit.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_HQPursuit.c
@@ -41,6 +41,13 @@
if (slot == 0) return ERR_HQPursuitRole.INTERCEPT;
if (slot == 2 && confidence >= 0.8 && uncertainty <= 100) return ERR_HQPursuitRole.CONTAIN;
return ERR_HQPursuitRole.SEARCH;
+ }
+ // Radius of one native search grid, copied from delivered uncertainty when
+ // the task is queued. This does not enlarge the reported swept-ground proof.
+ static float SearchRadius(float uncertainty)
+ {
+ if (!Finite(uncertainty) || uncertainty < 0) return 0;
+ return Math.Clamp(uncertainty, 45, 110);
}
static float LifetimeSeconds(ERR_HQPursuitRole role)
{
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_HQTask.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_HQTask.c
@@ -12,6 +12,7 @@
int BeliefId;
int BeliefRevision;
ERR_HQPursuitRole Role;
+ float SearchRadiusMetres;
vector Destination;
float QueuedAt;
float ExpiresAt;
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_NativePatrolHQProbe.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_NativePatrolHQProbe.c
@@ -20,6 +20,7 @@
if (!belief) return false;
ERR_HQTask task = new ERR_HQTask();
task.BeliefId = belief.BeliefId; task.BeliefRevision = belief.BeliefRevision;
+ task.SearchRadiusMetres = ERR_HQPursuitRules.SearchRadius(belief.Radius);
task.Destination = order.Position; task.QueuedAt = order.ObservedAt;
task.ExpiresAt = order.ObservedAt + 120000;
unit.PendingTask = task;
--- a/tests/test_hq_delivered_task.py
+++ b/tests/test_hq_delivered_task.py
@@ -13,7 +13,7 @@
for typ in ('ERR_HQUnit','ERR_HQTask','ERR_GameMode','ERR_HQReport','ERR_HQBelief','ERR_ForceRecord','ERR_HQHuntWindow','AIWaypoint'):
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
s=re.sub(r'foreach \((ERR_HQUnit\*) (\w+) : ([^)]+)\)',r'for (\1 \2 : \3)',s)
- for name in ('unit','other','task','planned','order','belief','record','mode','Group','hunt','m_Mode','lostActive','lostPending'):
+ for name in ('unit','other','task','planned','order','belief','record','mode','Group','hunt','waypoint','m_Mode','lostActive','lostPending'):
s=s.replace(name+'.',name+'->')
s=s.replace('ResourceName ', 'int ').replace('.IsEmpty()', '.empty()')
s=s.replace('PendingOrder.', 'PendingOrder->').replace('Assignment.', 'Assignment->')
@@ -28,7 +28,7 @@
if 'protected void ReleaseLostForce(' in P.read_text(): methods=cpp(body(P,'protected void ReleaseLostForce('))+'\n'+methods
missing=cpp(body(P,'if (!unit.Group && !unit.Uplink)')).replace('m_Units.Remove(i)', 'm_Units.erase(m_Units.begin()+i)')
cleanup=cpp(body(P,' foreach (ERR_HQUnit unit : m_Units)\n {\n if (!unit.Assignment && !unit.PendingOrder) continue;\n if (!unit.PlanEligible || assigned > globalLimit)'))
-R=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQPursuit.c'
+R=Path(os.environ.get('ERR_HQ_PURSUIT_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQPursuit.c'))
rules='\n'.join(cpp(body(R,x)) for x in ('static bool Finite(', 'static bool PositionValid(', 'static bool Actionable(', 'static bool Heading(', 'static ERR_HQPursuitRole Role('))
eligible=cpp(body(ROAD,'static bool Eligible('))
run(r'''
@@ -37,6 +37,7 @@
#include <string>
#include <algorithm>
#include <cmath>
+#include <limits>
using string=std::string;
template<class... T>string Format(T...){return "order";}
struct vector{float x=0,y=0,z=0;float& operator[](int i){return i==0?x:(i==1?y:z);}float Length(){return std::sqrt(x*x+y*y+z*z);}};
@@ -48,10 +49,10 @@
};
struct Math{static float Min(float a,float b){return std::min(a,b);}static float Max(float a,float b){return std::max(a,b);}};
struct ERR_HQTask{string SecuritySite;bool RoadIntercept=false,ReleaseOnly=false;ERR_HQTask* ReplacesTask=nullptr;string WatchSourceId;int WatchMessageId=0,WatchRequestId=0;
- int BeliefId=1,BeliefRevision=2;ERR_HQPursuitRole Role=ERR_HQPursuitRole::SEARCH;vector Destination{40,0,50};float QueuedAt=0,ExpiresAt=60000;bool Investigation(){return !WatchSourceId.empty();}string Name(){return "search";}};
+ int BeliefId=1,BeliefRevision=2;ERR_HQPursuitRole Role=ERR_HQPursuitRole::SEARCH;vector Destination{40,0,50};float SearchRadiusMetres=60,QueuedAt=0,ExpiresAt=60000;bool Investigation(){return !WatchSourceId.empty();}string Name(){return "search";}};
struct ERR_HQReport{string TravelMode="on foot";int MessageId=10;float ObservedAt=0;};
struct ERR_HQRoute{int MessageId=20;};
-struct AIWaypoint{int resource=0,deletes=0;static AIWaypoint* Cast(AIWaypoint* p){return p;}};
+struct AIWaypoint{int resource=0,deletes=0,configured=0;float radius=30;void SetCompletionRadius(float r){assert(!configured);radius=r;configured++;}static AIWaypoint* Cast(AIWaypoint* p){return p;}};
struct SCR_AIGroup{int removals=0,additions=0;AIWaypoint* last=nullptr;void RemoveWaypoint(AIWaypoint* w){removals++;last=w;}void AddWaypointAt(AIWaypoint* w,int at){assert(at==0);additions++;last=w;}};
struct ERR_ForceRecord{bool Eliminated=false;int Duty=2,HomeDuty=1;vector DutyTarget,HomePosition;};
struct Ledger{ERR_ForceRecord record;ERR_ForceRecord* FindGroup(SCR_AIGroup*){return &record;}};
@@ -85,6 +86,23 @@
void Retained(){assert(unit.Task==&active&&unit.Assignment==&oldReport&&unit.Waypoint==&previous&&previous.deletes==0&&group.removals==0&&!unit.PendingTask&&!unit.PendingOrder&&!unit.Downlink);}
};
int main(){
+ // The copied radius is used before installation even if current uncertainty grows.
+ for(float radius:{45.f,60.f,110.f}){Fixture f;f.next.SearchRadiusMetres=radius;f.belief.Radius=500;
+ float deadline=f.next.ExpiresAt;f.hq.DeliverOrder(&f.unit,1000);
+ assert(f.group.additions==1&&f.unit.Waypoint->radius==radius&&f.unit.Waypoint->configured==1);
+ assert(f.next.SearchRadiusMetres==radius&&f.next.ExpiresAt==deadline);}
+ // Malformed snapshot cannot spawn or replace the delivered previous assignment.
+ for(float radius:{0.f,44.99f,110.01f,-1.f,std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::infinity()}){
+ Fixture f;f.next.SearchRadiusMetres=radius;f.hq.DeliverOrder(&f.unit,1000);f.Retained();assert(ERR_Actors::spawns==0);}
+ // Missing patrol, road/security defend, interception and containment keep native defaults.
+ for(int kind=0;kind<5;kind++){Fixture f;f.next.SearchRadiusMetres=std::numeric_limits<float>::quiet_NaN();
+ if(kind==0)f.next.WatchSourceId="missing-original";
+ if(kind==1)f.next.RoadIntercept=true;
+ if(kind==2)f.next.SecuritySite="site";
+ if(kind==3)f.next.Role=ERR_HQPursuitRole::INTERCEPT;
+ if(kind==4){f.next.Role=ERR_HQPursuitRole::CONTAIN;f.belief.Confidence=.9f;}
+ f.hq.DeliverOrder(&f.unit,1000);assert(f.group.additions==1&&f.unit.Waypoint->radius==30&&f.unit.Waypoint->configured==0);}
+
// Alarm-only deliveries retain their weak SEARCH/mobile policy even if a queued task is tampered with.
for(int invalid=0;invalid<5;invalid++){Fixture f;f.belief.Kind=ERR_HQMemoryKind::ALARM;
ERR_HQUnit other;ERR_HQTask otherTask;ERR_HQReport alarm;alarm.TravelMode="heard vehicle alarm";
@@ -217,3 +235,6 @@
}
''')
print('PASS: actual delivered-task lineage, expiry, old-order retention, native commit, release, road precision expiry and duplicate delivery')
+
+delivery=body(P,'protected void DeliverOrder(')
+assert delivery.index('waypoint.SetCompletionRadius(task.SearchRadiusMetres)') < delivery.index('unit.Release();') < delivery.index('unit.Group.AddWaypointAt')
--- a/tests/test_hq_planner_budget.py
+++ b/tests/test_hq_planner_budget.py
@@ -44,13 +44,14 @@
namespace ERR_HQMemoryKind{enum{SIGHTING,GUNFIRE,ALARM};}
namespace ERR_HQPursuitRole{enum{SEARCH,INTERCEPT,CONTAIN};}
struct ERR_HQReport{int id=0;ERR_HQReport* Snapshot(){return this;}};
-struct ERR_HQTask{int BeliefId=0,BeliefRevision=0,Role=0,ApproachBearing=0;bool RoadIntercept=false;Vec Destination;float QueuedAt=0,ExpiresAt=0;string Name(){return "task";}};
+struct ERR_HQTask{int BeliefId=0,BeliefRevision=0,Role=0,ApproachBearing=0;bool RoadIntercept=false;Vec Destination;float SearchRadiusMetres=0,QueuedAt=0,ExpiresAt=0;string Name(){return "task";}};
struct ERR_HQRoute{int MessageId=0;string Payload;};
struct Group{Vec pos;Vec GetOrigin(){return pos;}};
struct ERR_HQUnit{int Id=0,Radio=0,HomeId=0,ForceSourceId=0;struct Group* Group=nullptr;ERR_HQReport* Assignment=nullptr;ERR_HQReport* PendingOrder=nullptr;ERR_HQTask* PendingTask=nullptr;ERR_HQRoute* Downlink=nullptr;bool PlanFighting=false,PlanEligible=true,HomeGuard=false;Vec PlanPosition;};
struct ERR_HQBelief{int BeliefId=0,BeliefRevision=1,MessageId=1,Kind=0;float Confidence=.7,ObservedAt=1000,Radius=100;Vec Position,PredictedPosition,Velocity;bool HasVelocity=true;ERR_HQReport* Report=nullptr;};
struct ERR_HQHuntWindow{float NextHunt=0;};
struct ERR_HQPursuitRules{
+ static float SearchRadius(float radius){return std::clamp(radius,45.f,110.f);}
static bool Actionable(float c,float age,float r){return c>=.15&&age>=0&&r<=2000;}
static bool Heading(Vec,bool h){return h;}
static int Role(float,float,float,bool h,int slot){return h?slot:0;}
@@ -91,7 +92,7 @@
}
};
void Equal(Setup&a,Setup&b){assert(a.h.m_Journal.writes==b.h.m_Journal.writes&&a.h.m_Coverage.selections==b.h.m_Coverage.selections&&a.h.m_HomeAvailable.v==b.h.m_HomeAvailable.v);
- for(int i=0;i<128;i++){auto x=a.units[i].PendingTask,y=b.units[i].PendingTask;assert(bool(x)==bool(y));if(!x)continue;assert(x->BeliefId==y->BeliefId&&x->Role==y->Role&&x->RoadIntercept==y->RoadIntercept&&x->Destination==y->Destination&&x->ExpiresAt==y->ExpiresAt&&x->ApproachBearing==y->ApproachBearing);}
+ for(int i=0;i<128;i++){auto x=a.units[i].PendingTask,y=b.units[i].PendingTask;assert(bool(x)==bool(y));if(!x)continue;assert(x->SearchRadiusMetres==y->SearchRadiusMetres&&x->BeliefId==y->BeliefId&&x->Role==y->Role&&x->RoadIntercept==y->RoadIntercept&&x->Destination==y->Destination&&x->ExpiresAt==y->ExpiresAt&&x->ApproachBearing==y->ApproachBearing);}
for(auto [id,window]:a.h.hunts)assert(window.NextHunt==b.h.hunts[id].NextHunt);
}
int main(){
--- a/tests/test_hq_pursuit.py
+++ b/tests/test_hq_pursuit.py
@@ -2,7 +2,7 @@
import os, re
from pathlib import Path
from policy_harness import ROOT, body, run
-P = ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQPursuit.c'
+P = Path(os.environ.get('ERR_HQ_PURSUIT_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQPursuit.c'))
source = P.read_text()
# Exercise the real planner expression against the same actual policy methods.
HQ = Path(os.environ.get('ERR_HQ_SOURCE', ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'))
@@ -13,7 +13,7 @@
methods = '\n'.join(body(P, signature) for signature in (
'static bool Finite(', 'static bool PositionValid(', 'static int Limit(', 'static bool Actionable(',
'static bool Heading(', 'static ERR_HQPursuitRole Role(', 'static float LifetimeSeconds(',
- 'static float TaskLifetimeSeconds(', 'static bool CanCommit(', 'static int Bearing(', 'static bool BearingAllowed(', 'static vector Direction(',
+ 'static float SearchRadius(', 'static float TaskLifetimeSeconds(', 'static bool CanCommit(', 'static int Bearing(', 'static bool BearingAllowed(', 'static vector Direction(',
'static vector Objective(', 'static float Score(', 'static bool Better('))
methods = methods.replace('Math.', 'Math::').replace('vector.', 'vector::')
methods = methods.replace('ERR_HQPursuitRole.', 'ERR_HQPursuitRole::')
@@ -55,6 +55,10 @@
ERR_HQPursuitRole PlannerRole(Belief belief,float age,int slot){return '''+planner_role+r''';}
bool near(float a,float b){return std::abs(a-b)<0.001f;}
int main(){
+ assert(Rules::SearchRadius(0)==45&&Rules::SearchRadius(44.99)==45&&Rules::SearchRadius(45)==45);
+ assert(Rules::SearchRadius(73)==73&&Rules::SearchRadius(110)==110&&Rules::SearchRadius(2000)==110);
+ assert(Rules::SearchRadius(-1)==0&&Rules::SearchRadius(std::numeric_limits<float>::quiet_NaN())==0&&Rules::SearchRadius(std::numeric_limits<float>::infinity())==0);
+
using Role=ERR_HQPursuitRole;
assert(Rules::Limit(-1)==0&&Rules::Limit(0)==0&&Rules::Limit(1)==1);
assert(Rules::Limit(3)==3&&Rules::Limit(8)==3);
@@ -186,3 +190,27 @@
assert forbidden not in source, f'pursuit must consume snapshots only: {forbidden}'
assert all(len(line)<120 for line in source.splitlines())
print('PASS: actual bounded pursuit roles/objectives/scores, stale search degradation, physical/busy/home guards, finite lifetime and two-bearing escape gap; no hidden player reads or spawning')
+
+for signature in ('protected void Assign(', 'protected void Retask('):
+ section=body(HQ,signature)
+ line=next(line.strip() for line in section.splitlines() if 'task.SearchRadiusMetres =' in line)
+ line=line.replace('ERR_HQPursuitRules.', 'Rules::').replace('ERR_HQPursuitRole.', 'Role::')
+ run(r"""
+#include <cassert>
+#include <algorithm>
+#include <cmath>
+#include <limits>
+enum class Role {SEARCH,INTERCEPT,CONTAIN};
+struct Math{static float Clamp(float n,float a,float b){return std::clamp(n,a,b);}};
+struct Rules{
+"""+body(P,'static bool Finite(')+body(P,'static float SearchRadius(').replace('Math.','Math::')+r"""
+};
+struct Task{float SearchRadiusMetres=0;};struct Belief{float Radius=0;};
+void Queue(Task& task,Belief& belief,Role role,bool roadIntercept){
+"""+line+r"""
+}
+int main(){for(float radius:{0.f,60.f,500.f}){Task task;Belief belief{radius};Queue(task,belief,Role::SEARCH,false);float copied=Rules::SearchRadius(radius);assert(task.SearchRadiusMetres==copied);belief.Radius=1900;assert(task.SearchRadiusMetres==copied);}
+"""+(r"""for(Role role:{Role::INTERCEPT,Role::CONTAIN}){Task task;Belief belief{100};Queue(task,belief,role,false);assert(task.SearchRadiusMetres==0);}Task road;Belief belief{100};Queue(road,belief,Role::SEARCH,true);assert(road.SearchRadiusMetres==0);""" if 'Assign' in signature else '')+r"""
+}
+""")
+print('PASS: actual finite45..110 radius and both ordinary search queue snapshot expressions')
@@ -0,0 +1,65 @@
{
"approved": true,
"blockers": [],
"review_mode": "Read-only independent source/test/log/hash inspection; reviewer ran no compiler/tests/native.",
"patch_sha256": "6f9bcc7c4f005406aca4d327b1d1d4628574ed079f7a746f9ff638756b1dbdee",
"source_manifest_sha256": "ca18054aa5d77971af4ce9c6c4c50cd2f638fb6f135629022acb439c626b65d0",
"files": {
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c": {
"baseline_sha256": "d873b5b689e918f8c907918d673304a184ac2e4a4cb6face527b37a67821ee9f",
"proposal_sha256": "f9b1a44a5ef135d5944dd643f6435bea4f913915c3b2e689724bab0df7d18d8c",
"index_sha256": "1f129b979abcb86ee10622e2866aa987807f070f2ab2d818af015e12a8bcfd8c",
"index_absent": false
},
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQPursuit.c": {
"baseline_sha256": "ab6ca0e1a7f36c104e664db06a74c80a528cbfd1de599af42e2194ef0a4f4ca3",
"proposal_sha256": "41b0d69deda08535fd24fb88e85a7632c67b5cb99e47d773bc9803415a57c041",
"index_sha256": "ab6ca0e1a7f36c104e664db06a74c80a528cbfd1de599af42e2194ef0a4f4ca3",
"index_absent": false
},
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQTask.c": {
"baseline_sha256": "1147904bae6d72e9de47ce17bdada3dab489de4ed6e781dbf15bdbb30599ff6e",
"proposal_sha256": "16ed213344c00ec80080fb6866136333c11115e99f402d455033b686ecec8dfe",
"index_sha256": "1147904bae6d72e9de47ce17bdada3dab489de4ed6e781dbf15bdbb30599ff6e",
"index_absent": false
},
"addons/core/Scripts/Game/ERR/10_AI/ERR_NativePatrolHQProbe.c": {
"baseline_sha256": "a58f57530e333976dc96fc5a0f9cb6cf0cb069c55788824950c5fb2f965d8e26",
"proposal_sha256": "6f63edca662a6f83a0874a104b030823cbbe1c4260ec3b41368899f738981efb",
"index_sha256": "a58f57530e333976dc96fc5a0f9cb6cf0cb069c55788824950c5fb2f965d8e26",
"index_absent": false
},
"tests/test_hq_delivered_task.py": {
"baseline_sha256": "076aecdb0ff0d614cadbffedf9d9f8cbb1383fb94a468bd92050b31251bcbce2",
"proposal_sha256": "22ac74b16ab949330a82b9384e8c93e1cfbfda59d009d3bb921407ff1848555f",
"index_sha256": "076aecdb0ff0d614cadbffedf9d9f8cbb1383fb94a468bd92050b31251bcbce2",
"index_absent": false
},
"tests/test_hq_planner_budget.py": {
"baseline_sha256": "c9e6b85a3d25267c187f503f903136a16aee382e9d842f61ff88c550b5bb4e51",
"proposal_sha256": "53578da6fb82f4cc4210169332b1f54ac349fab05cce5513be7328218edd264a",
"index_sha256": "c9e6b85a3d25267c187f503f903136a16aee382e9d842f61ff88c550b5bb4e51",
"index_absent": false
},
"tests/test_hq_pursuit.py": {
"baseline_sha256": "7ccbf51c7317097d9db6a66cc5be4bec67e542187f0fce5b4f3750bdc4311733",
"proposal_sha256": "90171769ac65c89bc1fd101252dd6327df327a5e3cb54fdc1c5e713303af855c",
"index_sha256": "7ccbf51c7317097d9db6a66cc5be4bec67e542187f0fce5b4f3750bdc4311733",
"index_absent": false
}
},
"test_results_sha256": "91dad71d2322c856365e692f587955db71ed598dbd121b855f5ac8be2a48f5dd",
"checks": [
"Finite SearchRadius rejects invalid/negative input then clamps delivered uncertainty to45..110; actual Assign and Retask copy once into detached task.",
"Ordinary SEARCH only; delivery validates copied radius before spawning or releasing old task. Exactly one setter precedes native installation. Live uncertainty changes do not mutate queued radius.",
"Missing-patrol, road/site-security, INTERCEPT/CONTAIN preserve original waypoint defaults. WORKBENCH direct patrol fixture initializes new SEARCH field.",
"SweepPresent, Sweep, AcceptSweep and ReleaseUnit exact unchanged bytes; original center40m,30s quiet presence, physical roster and radio delivery proof remain.",
"Six author focused programs green. Inspected actual lifecycle and queue-expression tests. Baseline compiles then fails intended runtime radius assertion; omitted validation and omitted Retask copy mutations compile and fail runtime assertions.",
"Exact seven proposal/baseline hashes, patch, final result/log hashes and installed native API/resource proof hashes verified."
],
"limits": [
"Actual C++ policy/lifecycle harnesses double native APIs; no native Enforce compilation or physical search qualification.",
"45..110 bounds search size, not execution cost. Larger grid may reduce conservative center-dwell negative reports; no wider swept-area claim.",
"Existing user game preserved; no native or source mutations by reviewer."
]
}
@@ -0,0 +1,19 @@
check: /tmp/err-policy-l7i6rzv6/check.cpp:265: void Fixture::Retained(): Assertion `unit.Task==&active&&unit.Assignment==&oldReport&&unit.Waypoint==&previous&&previous.deletes==0&&group.removals==0&&!unit.PendingTask&&!unit.PendingOrder&&!unit.Downlink' failed.
Traceback (most recent call last):
File "/tmp/erri-search-radius-1611/tests/test_hq_delivered_task.py", line 34, in <module>
run(r'''
~~~^^^^^
#include <cassert>
^^^^^^^^^^^^^^^^^^
...<199 lines>...
}
^
''')
^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/err-policy-l7i6rzv6/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,20 @@
check: /tmp/err-policy-wbaesr0g/check.cpp:22: int main(): Assertion `task.SearchRadiusMetres==copied' failed.
PASS: actual bounded pursuit roles/objectives/scores, stale search degradation, physical/busy/home guards, finite lifetime and two-bearing escape gap; no hidden player reads or spawning
Traceback (most recent call last):
File "/tmp/erri-search-radius-1611/tests/test_hq_pursuit.py", line 198, in <module>
run(r"""
~~~^^^^^
#include <cassert>
^^^^^^^^^^^^^^^^^^
...<14 lines>...
}
^
""")
^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/err-policy-wbaesr0g/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,17 @@
from pathlib import Path
import os,subprocess,json,hashlib
r=Path('/tmp/erri-search-radius-1611');live=Path('/home/jakub/devel/escapistsrereintroduced');ai=Path('addons/core/Scripts/Game/ERR/10_AI');e=os.environ.copy();e.update(PYTHONDONTWRITEBYTECODE='1',PYTHONPATH=str(live/'tests'),ERR_HQ_SOURCE=str(r/ai/'ERR_HQ.c'),ERR_HQ_PURSUIT_SOURCE=str(r/ai/'ERR_HQPursuit.c'));results=[]
def run(name,test,override=None,red=False):
env=e.copy();env.update(override or {});p=r/'tests'/test
if not p.exists():p=live/'tests'/test
log=r/(name+'.log')
with log.open('w') as f:x=subprocess.run(['python3',str(p)],env=env,stdout=f,stderr=subprocess.STDOUT)
text=log.read_text();ok=x.returncode==0 if not red else x.returncode!=0 and 'SIGABRT' in text and 'error:' not in text
results.append({'name':name,'exit':x.returncode,'expected_result_observed':ok,'log_sha256':hashlib.sha256(log.read_bytes()).hexdigest()});assert ok,(name,text)
for t in ['test_hq_pursuit.py','test_hq_delivered_task.py','test_hq_planner_budget.py','test_hq_task_reservations.py','test_hq_assignment_lifecycle.py','test_hq_uplink_fairness.py']:run(t.removesuffix('.py')+'-green',t)
run('baseline-delivery-red','test_hq_delivered_task.py',{'ERR_HQ_SOURCE':str(r/'baseline'/ai/'ERR_HQ.c')},True)
source=(r/ai/'ERR_HQ.c').read_text()
for name,old,new,test in [('recompute-on-delivery','waypoint.SetCompletionRadius(task.SearchRadiusMetres)','waypoint.SetCompletionRadius(m_Memory.GetById(task.BeliefId).Radius)','test_hq_delivered_task.py'),('invalid-radius-admitted','if (memorySearch && (!ERR_HQPursuitRules.Finite(task.SearchRadiusMetres)','if (false && (!ERR_HQPursuitRules.Finite(task.SearchRadiusMetres)','test_hq_delivered_task.py'),('retask-not-copied','task.SearchRadiusMetres = ERR_HQPursuitRules.SearchRadius(belief.Radius);','task.SearchRadiusMetres = 0;','test_hq_pursuit.py')]:
if name=='recompute-on-delivery':continue # pointer adapter would need unrelated mutation-specific adaptation
assert old in source;p=r/'mutations'/name/'ERR_HQ.c';p.parent.mkdir(parents=True,exist_ok=True);p.write_text(source.replace(old,new,1));run(name+'-red',test,{'ERR_HQ_SOURCE':str(p)},True)
(r/'test-results.json').write_text(json.dumps(results,indent=2)+'\n');print(json.dumps(results,indent=2))
@@ -0,0 +1,22 @@
{
"native_grid": {
"path": "/tmp/erri-vanilla-stock/scripts/Game/AI/Behavior/SCR_AISearchAndDestroyActivity.c",
"sha256": "51f4343ee3e8fa90edea9a9a7d73581debfccc961a2e65dc87043289a5bd6fe1",
"lines": [
130,
143,
154
],
"meaning": "GetCompletionRadius supplies actionRadius/square half-width for investigation grid. Larger radius increases bounded cell/navigation work; no FPS improvement claim."
},
"native_default": {
"path": "/tmp/erri-vanilla-stock/Prefabs/AI/Waypoints/AIWaypoint_SearchAndDestroy.et",
"sha256": "55e46bd62175f3f2b4c5f203bb161b339648f5c1a1b53fcc6b40193c72dcf2a2",
"radius": 30
},
"native_setter_example": {
"path": "/tmp/erri-vanilla-stock/scripts/Game/Components/Spawner/CatalogSpawner/SCR_CatalogEntitySpawnerComponent.c",
"sha256": "19069dcac3b76d0400719026646154346e2dba57d90e414fcbbc993122aa7695",
"line": 1248
}
}
@@ -0,0 +1,56 @@
{
"files": {
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c": {
"baseline_sha256": "d873b5b689e918f8c907918d673304a184ac2e4a4cb6face527b37a67821ee9f",
"proposal_sha256": "f9b1a44a5ef135d5944dd643f6435bea4f913915c3b2e689724bab0df7d18d8c",
"index_sha256": "1f129b979abcb86ee10622e2866aa987807f070f2ab2d818af015e12a8bcfd8c",
"index_absent": false
},
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQPursuit.c": {
"baseline_sha256": "ab6ca0e1a7f36c104e664db06a74c80a528cbfd1de599af42e2194ef0a4f4ca3",
"proposal_sha256": "41b0d69deda08535fd24fb88e85a7632c67b5cb99e47d773bc9803415a57c041",
"index_sha256": "ab6ca0e1a7f36c104e664db06a74c80a528cbfd1de599af42e2194ef0a4f4ca3",
"index_absent": false
},
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQTask.c": {
"baseline_sha256": "1147904bae6d72e9de47ce17bdada3dab489de4ed6e781dbf15bdbb30599ff6e",
"proposal_sha256": "16ed213344c00ec80080fb6866136333c11115e99f402d455033b686ecec8dfe",
"index_sha256": "1147904bae6d72e9de47ce17bdada3dab489de4ed6e781dbf15bdbb30599ff6e",
"index_absent": false
},
"addons/core/Scripts/Game/ERR/10_AI/ERR_NativePatrolHQProbe.c": {
"baseline_sha256": "a58f57530e333976dc96fc5a0f9cb6cf0cb069c55788824950c5fb2f965d8e26",
"proposal_sha256": "6f63edca662a6f83a0874a104b030823cbbe1c4260ec3b41368899f738981efb",
"index_sha256": "a58f57530e333976dc96fc5a0f9cb6cf0cb069c55788824950c5fb2f965d8e26",
"index_absent": false
},
"tests/test_hq_delivered_task.py": {
"baseline_sha256": "076aecdb0ff0d614cadbffedf9d9f8cbb1383fb94a468bd92050b31251bcbce2",
"proposal_sha256": "22ac74b16ab949330a82b9384e8c93e1cfbfda59d009d3bb921407ff1848555f",
"index_sha256": "076aecdb0ff0d614cadbffedf9d9f8cbb1383fb94a468bd92050b31251bcbce2",
"index_absent": false
},
"tests/test_hq_planner_budget.py": {
"baseline_sha256": "c9e6b85a3d25267c187f503f903136a16aee382e9d842f61ff88c550b5bb4e51",
"proposal_sha256": "53578da6fb82f4cc4210169332b1f54ac349fab05cce5513be7328218edd264a",
"index_sha256": "c9e6b85a3d25267c187f503f903136a16aee382e9d842f61ff88c550b5bb4e51",
"index_absent": false
},
"tests/test_hq_pursuit.py": {
"baseline_sha256": "7ccbf51c7317097d9db6a66cc5be4bec67e542187f0fce5b4f3750bdc4311733",
"proposal_sha256": "90171769ac65c89bc1fd101252dd6327df327a5e3cb54fdc1c5e713303af855c",
"index_sha256": "7ccbf51c7317097d9db6a66cc5be4bec67e542187f0fce5b4f3750bdc4311733",
"index_absent": false
}
},
"new_test_registration": [],
"native_validation": "Not run; existing user game preserved. No native compilation or behavior/FPS proof.",
"patch_sha256": "6f9bcc7c4f005406aca4d327b1d1d4628574ed079f7a746f9ff638756b1dbdee",
"test_results_sha256": "91dad71d2322c856365e692f587955db71ed598dbd121b855f5ac8be2a48f5dd",
"unchanged_method_sha256": {
"protected bool SweepPresent(": "9af91ba0b0a802b1225f95d956235d5e5382343440759eb2eb2a9f742d2c3198",
"protected void Sweep(": "36b6d45d5710c68c76076377c26f8b3448af6b161a2bf78b948e4f29c84b369b",
"protected void AcceptSweep(": "96549b9bb41cd4623b3119485b27e16626b6c42e150bf95dfe93e6a6159a0e2d",
"protected void ReleaseUnit(": "405cd08b26befee2a6ff789a1e0454d5b40f9caaaf25127a16cf89a84d0efd46"
}
}
@@ -0,0 +1,56 @@
[
{
"name": "test_hq_pursuit-green",
"exit": 0,
"expected_result_observed": true,
"log_sha256": "cfa9cf0d5c8184ee60c0ea4895eb9c9ca7755fea336d6fb565b0eebfaf9b3525"
},
{
"name": "test_hq_delivered_task-green",
"exit": 0,
"expected_result_observed": true,
"log_sha256": "3e4c1177f828b2b47baa6fb274ed2ac27ca33fee42e75f49dfbd0124cd5049dd"
},
{
"name": "test_hq_planner_budget-green",
"exit": 0,
"expected_result_observed": true,
"log_sha256": "1b087e6a04079b13440799675b81a19b67b50c04b6aaa14bfd806d99a7fa2533"
},
{
"name": "test_hq_task_reservations-green",
"exit": 0,
"expected_result_observed": true,
"log_sha256": "b62b1a7d3a45a620144126f1581db6b290a6ccffc04e12f4b607f66e5303d0ae"
},
{
"name": "test_hq_assignment_lifecycle-green",
"exit": 0,
"expected_result_observed": true,
"log_sha256": "261750e7934a5b0fc94357eba49f2090491e27ed8304d6eeb8c90cfb6c04b0ab"
},
{
"name": "test_hq_uplink_fairness-green",
"exit": 0,
"expected_result_observed": true,
"log_sha256": "34d1246df1132ab32d36dc96a65cc5c5ae6ff113ba5d9f22c7ae3847fd8e54d1"
},
{
"name": "baseline-delivery-red",
"exit": 1,
"expected_result_observed": true,
"log_sha256": "5964c876c816a57a80d6c06378b002f4997867ba037e9c7a374a458f1547aa5e"
},
{
"name": "invalid-radius-admitted-red",
"exit": 1,
"expected_result_observed": true,
"log_sha256": "8bf0074be58c24469b7b7a68e5ce2fe726dbcbbeeb889febeb35262cb03441fa"
},
{
"name": "retask-not-copied-red",
"exit": 1,
"expected_result_observed": true,
"log_sha256": "0c2b986c54a93da1c556b35ef8b33bb27f02d9f5386890859626aaa321ab87f0"
}
]
@@ -0,0 +1 @@
PASS: actual failed-retask deadline rollback plus assignment cancellation/release preserves radio ownership, native waypoint lifecycle and failed-order retry boundaries
@@ -0,0 +1 @@
PASS: actual delivered-task lineage, expiry, old-order retention, native commit, release, road precision expiry and duplicate delivery
@@ -0,0 +1 @@
PASS: actual full planner selection/commit body, 360 equivalence scenarios, orders/reservations/cooldowns identical; one128-unit scan vs >10000 repeated unit visits; no FPS claim
@@ -0,0 +1,2 @@
PASS: actual bounded pursuit roles/objectives/scores, stale search degradation, physical/busy/home guards, finite lifetime and two-bearing escape gap; no hidden player reads or spawning
PASS: actual finite45..110 radius and both ordinary search queue snapshot expressions
@@ -0,0 +1 @@
PASS: actual HQ active/pending reservations, interruption, committed replacement, bounded coverage and unchanged force counts
@@ -0,0 +1 @@
PASS: actual report methods reproduce old starvation; status-first scheduler delivers check-ins and requires 30s without combat or actual visible contacts before negative reports