--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c
@@ -779,6 +779,7 @@
  }
  protected void ReleaseUnit(ERR_HQUnit unit, float now)
  {
+  if (unit.Assignment) RememberCompletedHunt(unit.Task, now);
   FinishWatch(unit.Task, now); FinishWatch(unit.PendingTask, now);
   unit.Release();
  }
@@ -937,22 +938,50 @@
   if (task.BeliefId != beliefId || !(task.ExpiresAt > now)) return now + 15000;
   return task.ExpiresAt + 180000;
  }
+ // Completion is historical: later loss of another squad must not erase this
+ // belief's completed-search quiet interval after the original task is cleared.
+ protected void RememberCompletedHunt(ERR_HQTask task, float now)
+ {
+  if (!task || task.ReleaseOnly || !task.SecuritySite.IsEmpty() || task.Investigation()) return;
+  if (!(task.ExpiresAt <= now)) return;
+  ERR_HQHuntWindow hunt = Hunt(task.BeliefId);
+  if (hunt) hunt.CompletedUntil = Math.Max(hunt.CompletedUntil, task.ExpiresAt + 180000);
+ }
+ // Releasing lost physical work and losing a radio order use the same finite
+ // surviving-commitment horizon. This never extends an already shorter hold.
+ protected void ReconsiderHunt(ERR_HQTask task, float now)
+ {
+  if (!task || task.ReleaseOnly || !task.SecuritySite.IsEmpty() || task.Investigation()) return;
+  float nextHunt = now + 15000;
+  foreach (ERR_HQUnit other : m_Units)
+  {
+   if (other.Assignment)
+   {
+    if (other.Task && other.Task.BeliefId == task.BeliefId) RememberCompletedHunt(other.Task, now);
+    nextHunt = Math.Max(nextHunt, HuntCommitment(other.Task, task.BeliefId, now));
+   }
+   if (other.PendingOrder) nextHunt = Math.Max(nextHunt, HuntCommitment(other.PendingTask, task.BeliefId, now));
+  }
+  // Keep both active and pending survivors' deadlines, or the short retry delay.
+  ERR_HQHuntWindow hunt = Hunt(task.BeliefId);
+  if (hunt) hunt.NextHunt = Math.Min(hunt.NextHunt, Math.Max(nextHunt, hunt.CompletedUntil));
+ }
+ // The original physical group is no longer available to fulfil its order.
+ protected void ReleaseLostForce(ERR_HQUnit unit, float now)
+ {
+  ref ERR_HQTask lostActive = unit.Task;
+  ref ERR_HQTask lostPending = unit.PendingTask;
+  ReleaseUnit(unit, now);
+  if (lostActive && lostActive.ExpiresAt > now) ReconsiderHunt(lostActive, now);
+  ReconsiderHunt(lostPending, now);
+ }
  protected void LostOrder(ERR_HQUnit unit, float now)
  {
   ref ERR_HQTask task = unit.PendingTask;
   unit.CancelPending();
   if (!task || task.ReleaseOnly || !task.SecuritySite.IsEmpty()) return;
   if (task.Investigation()) { FinishWatch(task, now); return; }
-  float nextHunt = now + 15000;
-  foreach (ERR_HQUnit other : m_Units)
-  {
-   if (other.Assignment) nextHunt = Math.Max(nextHunt, HuntCommitment(other.Task, task.BeliefId, now));
-   if (other.PendingOrder) nextHunt = Math.Max(nextHunt, HuntCommitment(other.PendingTask, task.BeliefId, now));
-  }
-  // A lost long-distance retask cannot prolong an older delivered hunt. Keep
-  // both active and pending survivors' own deadlines, or the short retry delay.
-  ERR_HQHuntWindow hunt = Hunt(task.BeliefId);
-  if (hunt) hunt.NextHunt = Math.Min(hunt.NextHunt, nextHunt);
+  ReconsiderHunt(task, now);
  }
  // Several squads may hear the same alarm. Reserve only one mobile search
  // across all alarm beliefs, including orders still travelling over radio.
@@ -1006,6 +1035,7 @@
   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 (unit.Assignment) RememberCompletedHunt(unit.Task, now);
   unit.Release();
   unit.Task = task;
   unit.Waypoint = waypoint;
@@ -1345,8 +1375,13 @@
   foreach (ERR_HQUnit unit : m_Units)
   {
    if (!unit.Assignment && !unit.PendingOrder) continue;
-   if (!unit.PlanEligible || assigned > globalLimit) { FinishWatch(unit.Task, now); FinishWatch(unit.PendingTask, now); unit.Release(); assigned--; continue; }
-   if (unit.Task && now >= unit.Task.ExpiresAt) { FinishWatch(unit.Task, now); unit.ReleaseAssignment(); }
+   if (!unit.PlanEligible || assigned > globalLimit)
+   {
+    if (!unit.PlanEligible) ReleaseLostForce(unit, now);
+    else ReleaseUnit(unit, now); // Capacity trimming preserves the ordinary hold.
+    assigned--; continue;
+   }
+   if (unit.Task && now >= unit.Task.ExpiresAt) { if (unit.Assignment) RememberCompletedHunt(unit.Task, now); FinishWatch(unit.Task, now); unit.ReleaseAssignment(); }
    if (unit.PendingTask && now >= unit.PendingTask.ExpiresAt) LostOrder(unit, now);
    if (unit.Task && unit.Task.SecuritySite.IsEmpty() && !unit.Task.Investigation() && !m_Memory.GetById(unit.Task.BeliefId)) unit.ReleaseAssignment();
    if (unit.PendingTask && !unit.PendingTask.ReleaseOnly && unit.PendingTask.SecuritySite.IsEmpty() && !unit.PendingTask.Investigation() && !m_Memory.GetById(unit.PendingTask.BeliefId)) unit.CancelPending();
@@ -1508,7 +1543,7 @@
     }
 				else if (delivered < 0) LostOrder(unit, now);
 			}
-			if (!unit.Group && !unit.Uplink) { ReleaseUnit(unit, now); m_Units.Remove(i); continue; }
+			if (!unit.Group && !unit.Uplink) { ReleaseLostForce(unit, now); m_Units.Remove(i); continue; }
    QueueReports(unit, now);
 		}
 		if (now >= m_NextAssignment) { PlanConvoy(now); PlanResupply(now); Assign(now); m_NextAssignment = now + 5000; }
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_HQTask.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_HQTask.c
@@ -38,4 +38,5 @@
  bool Investigation() { return !WatchSourceId.IsEmpty(); }
  int BeliefId;
  float NextHunt;
+ float CompletedUntil;
 }
--- 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'):
+ for name in ('unit','other','task','planned','order','belief','record','mode','Group','hunt','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->')
@@ -23,6 +23,10 @@
 units='\n'.join(cpp(body(P,x)) for x in ('void CancelPending(', 'ERR_HQTask PlannedTask(', 'void ReleaseAssignment(', 'void Release()'))
 methods='\n'.join(cpp(body(P,x)) for x in ('protected bool AlarmCommitted(', 'protected void FinishWatch(', 'protected void ReleaseUnit(', 'protected void LostOrder(', 'protected void DeliverOrder('))
 if 'protected float HuntCommitment(' in P.read_text(): methods=cpp(body(P,'protected float HuntCommitment('))+'\n'+methods
+if 'protected void ReconsiderHunt(' in P.read_text(): methods=cpp(body(P,'protected void ReconsiderHunt('))+'\n'+methods
+if 'protected void RememberCompletedHunt(' in P.read_text(): methods=cpp(body(P,'protected void RememberCompletedHunt('))+'\n'+methods
+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'
 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('))
@@ -59,7 +63,7 @@
 struct MineJob {int stops=0;void Stop(){stops++;}};
 struct ERR_HQUnit{MineJob* EngineerMine=nullptr;int Id=1;string ForceSourceId="original";SCR_AIGroup* Group=nullptr;AIWaypoint* Waypoint=nullptr;
  ERR_HQTask* Task=nullptr;ERR_HQTask* PendingTask=nullptr;ERR_HQReport* Assignment=nullptr;ERR_HQReport* PendingOrder=nullptr;
- ERR_HQRoute* Downlink=nullptr;void* PendingConvoyOrder=nullptr;bool HomeGuard=false,PendingCheckin=false,eligible=true,fighting=false,PlanEligible=true;
+ ERR_HQRoute* Downlink=nullptr;ERR_HQRoute* Uplink=nullptr;void* PendingConvoyOrder=nullptr;bool HomeGuard=false,PendingCheckin=false,eligible=true,fighting=false,PlanEligible=true;
  bool Eligible(ERR_GameMode*){return eligible&&Group;}
 '''+units+r'''
 };
@@ -70,10 +74,10 @@
 struct Memory{ERR_HQBelief* belief=nullptr;ERR_HQBelief* GetById(int id){return belief&&belief->BeliefId==id?belief:nullptr;}};
 struct Watch{bool empty=false;int finished=0;bool ArmedSearchBlocked(string,float){return empty;}bool FinishInvestigation(string,int,int,float){finished++;return true;}};
 struct Journal{int writes=0;void Write(string,string,int=0){writes++;}};
-struct ERR_HQHuntWindow{float NextHunt=200000;};
-struct HQ{ERR_GameMode* m_Mode;Memory m_Memory;Watch m_Watch;Journal m_Journal;std::vector<ERR_HQUnit*>m_Units;ERR_HQHuntWindow hunt;bool watchValid=true;
- bool Fighting(ERR_HQUnit* u){return u->fighting;}bool WatchTaskValid(ERR_HQTask*){return watchValid;}ERR_HQHuntWindow* Hunt(int){return &hunt;}
-'''+methods+'\nvoid Cleanup(float now,int assigned,int globalLimit){'+cleanup+'}'+r'''
+struct ERR_HQHuntWindow{float CompletedUntil=0;float NextHunt=200000;};
+struct HQ{ERR_GameMode* m_Mode;Memory m_Memory;Watch m_Watch;Journal m_Journal;std::vector<ERR_HQUnit*>m_Units;ERR_HQHuntWindow hunt,otherHunt;bool watchValid=true;
+ bool Fighting(ERR_HQUnit* u){return u->fighting;}bool WatchTaskValid(ERR_HQTask*){return watchValid;}ERR_HQHuntWindow* Hunt(int id){return id==2?&otherHunt:&hunt;}
+'''+methods+'\nvoid Cleanup(float now,int assigned,int globalLimit){'+cleanup+'}\nvoid RemoveMissing(float now){for(int i=int(m_Units.size())-1;i>=0;i--){ERR_HQUnit* unit=m_Units[i];'+missing+'}}'+r'''
 };
 struct Fixture{ERR_GameMode mode;HQ hq;ERR_HQUnit unit;SCR_AIGroup group;AIWaypoint previous;ERR_HQTask active,next;ERR_HQReport oldReport,newReport;ERR_HQRoute route;ERR_HQBelief belief;
  Fixture(){ERR_GameMode::current=&mode;ERR_Actors::refuse=false;ERR_Actors::spawns=0;hq.m_Mode=&mode;hq.m_Units={&unit};hq.m_Memory.belief=&belief;
@@ -117,6 +121,54 @@
  assert(f.mode.ledger.record.Duty==f.mode.ledger.record.HomeDuty);f.hq.Cleanup(1001,1,4);assert(f.previous.deletes==1&&f.unit.PendingTask==&f.next);}
  {Fixture f;f.active.BeliefId=1;f.next.ExpiresAt=1000;f.hq.Cleanup(1000,1,4);f.Retained();}
  {Fixture f;f.unit.PlanEligible=false;f.hq.Cleanup(1000,1,4);assert(!f.unit.Task&&!f.unit.PendingTask&&!f.unit.Downlink&&f.previous.deletes==1);}
+ // Loss of the only physical pursuit squad releases its unfulfilled long hold.
+ {Fixture f;f.unit.CancelPending();f.active.BeliefId=1;f.active.ExpiresAt=600000;f.hq.hunt.NextHunt=780000;f.unit.PlanEligible=false;
+ f.hq.Cleanup(10000,1,4);assert(f.hq.hunt.NextHunt==25000);assert(!f.unit.Task&&!f.unit.Assignment&&f.previous.deletes==1);}
+ // Native group deletion takes the earlier Tick cleanup path, before Assign.
+ {Fixture f;f.unit.CancelPending();f.active.BeliefId=1;f.active.ExpiresAt=600000;f.hq.hunt.NextHunt=780000;f.unit.Group=nullptr;
+ f.hq.RemoveMissing(10000);assert(f.hq.m_Units.empty()&&f.hq.hunt.NextHunt==25000&&!f.unit.Task&&f.previous.deletes==1);}
+ // Finishing an ordinary delivered search retains its completed-hunt cooldown.
+ {Fixture f;f.unit.CancelPending();f.active.BeliefId=1;f.active.ExpiresAt=10000;f.hq.hunt.NextHunt=190000;
+ f.hq.Cleanup(10000,1,4);assert(f.hq.hunt.NextHunt==190000&&!f.unit.Assignment);}
+ // Simultaneous expiry and unavailable-force detection cannot erase a completed
+ // hunt's quiet interval, including after loss of a pending same-belief retask.
+ for(int removed=0;removed<2;removed++)for(int pending=0;pending<3;pending++)for(int shorter=0;shorter<2;shorter++){
+ Fixture f;f.active.BeliefId=1;f.active.ExpiresAt=10000;f.hq.hunt.NextHunt=shorter?12000:190000;f.hq.otherHunt.NextHunt=780000;
+ if(pending==0)f.unit.CancelPending();else {f.next.BeliefId=pending==1?1:2;f.next.ExpiresAt=600000;}
+ f.unit.PlanEligible=false;if(removed){f.unit.Group=nullptr;f.hq.RemoveMissing(10000);}else f.hq.Cleanup(10000,1,4);
+ assert(f.hq.hunt.NextHunt==(shorter?12000:190000));assert(f.hq.otherHunt.NextHunt==(pending==2?25000:780000));
+ assert(!f.unit.Task&&!f.unit.PendingTask&&f.previous.deletes==1);
+ }
+ // A completed squad's floor survives a different lost squad in either cleanup
+ // order, whether the completed squad expires normally or is also unavailable.
+ for(int reverse=0;reverse<2;reverse++)for(int completedLost=0;completedLost<2;completedLost++){
+ Fixture f;f.unit.CancelPending();f.active.BeliefId=1;f.active.ExpiresAt=10000;f.unit.PlanEligible=!completedLost;f.hq.hunt.NextHunt=780000;
+ ERR_HQUnit lost;SCR_AIGroup lostGroup;ERR_HQTask lostTask;ERR_HQReport lostReport;lost.Group=&lostGroup;lost.Task=&lostTask;lost.Assignment=&lostReport;lost.PlanEligible=false;lostTask.BeliefId=1;lostTask.ExpiresAt=600000;
+ f.hq.m_Units.push_back(&lost);if(reverse)std::reverse(f.hq.m_Units.begin(),f.hq.m_Units.end());f.hq.Cleanup(10000,2,4);assert(f.hq.hunt.NextHunt==190000);
+ }
+ // A replacement can arrive before Assign processes the original expiry.
+ // Native commit must preserve completion history before clearing old ownership.
+ {Fixture f;f.active.BeliefId=1;f.active.ExpiresAt=10000;f.next.ExpiresAt=600000;f.hq.hunt.NextHunt=780000;
+ f.hq.DeliverOrder(&f.unit,10000);assert(f.unit.Task==&f.next&&f.hq.hunt.CompletedUntil==190000);
+ f.unit.PlanEligible=false;f.hq.Cleanup(11000,1,4);assert(f.hq.hunt.NextHunt==190000&&!f.unit.Task);}
+ // Only expired delivered pursuit work creates completion history.
+ for(int kind=0;kind<5;kind++){Fixture f;f.active.BeliefId=1;f.active.ExpiresAt=10000;
+ if(kind==0){f.active.ExpiresAt=10001;}if(kind==1){f.active.ReleaseOnly=true;}if(kind==2){f.active.SecuritySite="security";}if(kind==3){f.active.WatchSourceId="watch";}if(kind==4){f.unit.Assignment=nullptr;}
+ f.hq.ReleaseUnit(&f.unit,10000);assert(f.hq.hunt.CompletedUntil==0&&f.hq.otherHunt.CompletedUntil==0);}
+ // Capacity trimming is deliberately unchanged: it is not evidence of force loss.
+ {Fixture f;f.unit.CancelPending();f.active.BeliefId=1;f.active.ExpiresAt=600000;f.hq.hunt.NextHunt=780000;
+ f.hq.Cleanup(10000,2,1);assert(f.hq.hunt.NextHunt==780000&&!f.unit.Assignment);}
+ // Surviving active and pending same-belief commitments both retain their own
+ // horizon; foreign replacements cannot hide active work or prolong this hunt.
+ for(int scenario=0;scenario<5;scenario++){Fixture f;f.unit.CancelPending();f.active.BeliefId=1;f.active.ExpiresAt=600000;f.hq.hunt.NextHunt=780000;f.unit.PlanEligible=false;
+ ERR_HQUnit peer;SCR_AIGroup peerGroup;ERR_HQTask surviving,replacement;ERR_HQReport activeReport,pendingReport;peer.Group=&peerGroup;peer.Task=&surviving;peer.Assignment=&activeReport;peer.PendingTask=&replacement;peer.PendingOrder=&pendingReport;
+ surviving.BeliefId=1;surviving.ExpiresAt=120000;replacement.BeliefId=1;replacement.ExpiresAt=180000;
+ if(scenario==1)replacement.BeliefId=2;
+ if(scenario==2){surviving.SecuritySite="outpost";replacement.ReleaseOnly=true;}
+ if(scenario==3){peer.Assignment=nullptr;peer.PendingOrder=nullptr;}
+ if(scenario==4)f.hq.hunt.NextHunt=12000;
+ f.hq.m_Units.push_back(&peer);f.hq.Cleanup(10000,2,4);float expected=scenario==0?360000:scenario==1?300000:scenario==4?12000:25000;
+ assert(f.hq.hunt.NextHunt==expected);assert(peer.Task==&surviving);}
  // Expired undelivered retasks use the same bounded rollback as lost radio.
  // Both original and replacement expire here; no unfulfilled hunt owns 780s.
  {Fixture f;f.active.ExpiresAt=120000;f.next.ExpiresAt=600000;f.hq.hunt.NextHunt=780000;
--- a/tests/test_hq_assignment_lifecycle.py
+++ b/tests/test_hq_assignment_lifecycle.py
@@ -6,6 +6,8 @@
 unit='\n'.join(body(p,s) for s in ('void CancelPending(', 'ERR_HQTask PlannedTask(', 'void ReleaseAssignment(', 'void Release()'))
 lost='\n'.join(body(p,s) for s in ('protected void FinishWatch(', 'protected void LostOrder(')).replace('protected ', '')
 if 'protected float HuntCommitment(' in p.read_text(): lost=body(p,'protected float HuntCommitment(').replace('protected ', '')+'\n'+lost
+if 'protected void ReconsiderHunt(' in p.read_text(): lost=body(p,'protected void ReconsiderHunt(').replace('protected ', '')+'\n'+lost
+if 'protected void RememberCompletedHunt(' in p.read_text(): lost=body(p,'protected void RememberCompletedHunt(').replace('protected ', '')+'\n'+lost
 retask=body(p,'protected void Retask(')
 queue=retask[retask.index('   ERR_HQHuntWindow hunt = Hunt(belief.BeliefId);'):]
 queue=queue[:queue.index(';',queue.index('if (hunt)'))+1]
@@ -22,7 +24,7 @@
  code=code.replace('foreach (ERR_HQUnit other : m_Units)', 'for (ERR_HQUnit* other : m_Units)')
  code=re.sub(r'\b(mode|record|Group|unit|other|task|planned|hunt)\.', r'\1->', code)
  code=code.replace('mode->Forces().FindGroup', 'mode->Forces()->FindGroup')
- return code
+ return code.replace('other->Task.', 'other->Task->')
 run(r'''
 #include <cassert>
 #include <vector>
@@ -41,7 +43,7 @@
  static ERR_GameMode* Get(){return current;} Ledger* Forces(){return &ledger;}};
 ERR_GameMode* ERR_GameMode::current=nullptr;
 struct SCR_EntityHelper {static void DeleteEntityAndChildren(AIWaypoint* p){p->deleted++;}};
-struct ERR_HQHuntWindow {float NextHunt=0;};
+struct ERR_HQHuntWindow {float CompletedUntil=0;float NextHunt=0;};
 struct MineJob {int stops=0;void Stop(){stops++;}};
 struct ERR_HQUnit {MineJob* EngineerMine=nullptr;
  SCR_AIGroup* Group=nullptr;AIWaypoint* Waypoint=nullptr;
@@ -112,7 +114,8 @@
  // An unrelated pending replacement cannot conceal its original same-track task.
  replacement.BeliefId=8;owner.PendingTask=&pending;owner.PendingOrder=&r;h.window.NextHunt=1000000;
  h.LostOrder(&owner,30000);assert(h.window.NextHunt==580000);
- // Orphan task pointers, expired tasks and non-pursuit orders cannot hold a hunt.
+ // Orphan pointers and non-pursuit orders cannot hold a hunt. A still-owned
+ // expired active search preserves its completed interval.
  owner.Task=nullptr;owner.Assignment=nullptr;peer.Assignment=nullptr;peer.PendingOrder=nullptr;
  owner.PendingTask=&pending;owner.PendingOrder=&r;h.window.NextHunt=1000000;
  h.LostOrder(&owner,30000);assert(h.window.NextHunt==45000);
@@ -120,7 +123,7 @@
  peer.Assignment=&r;active.ExpiresAt=400000;active.ReleaseOnly=false;active.SecuritySite="";active.WatchSourceId="";
  if(kind==0){active.ReleaseOnly=true;}if(kind==1){active.SecuritySite="site";}if(kind==2){active.WatchSourceId="patrol";}if(kind==3){active.ExpiresAt=30000;}
  owner.PendingTask=&pending;owner.PendingOrder=&r;h.window.NextHunt=1000000;
- h.LostOrder(&owner,30000);assert(h.window.NextHunt==45000);}
+ h.LostOrder(&owner,30000);assert(h.window.NextHunt==(kind==3?210000:45000));}
  // Clamping only: even a surviving long commitment must not raise earlier pacing.
  active.ExpiresAt=400000;owner.PendingTask=&pending;owner.PendingOrder=&r;h.window.NextHunt=40000;
  h.LostOrder(&owner,30000);assert(h.window.NextHunt==40000);
--- a/tests/test_hq_task_reservations.py
+++ b/tests/test_hq_task_reservations.py
@@ -28,7 +28,7 @@
   s=s.replace(name+'.',name+'->')
  s=s.replace('PendingTask.', 'PendingTask->')
  s=s.replace('int total;','int total=0;').replace('bool found;','bool found=false;')
- return s
+ return s.replace('other->Task.', 'other->Task->')
 assign=body(P,'protected void Assign(')
 collect=loop(assign[assign.index('  array<vector> reserved = {};'):], 'foreach (ERR_HQUnit unit : m_Units)')
 retask=body(P,'protected void Retask(')
@@ -40,6 +40,8 @@
  assert 'ReserveUnit(existing, reserved, first, second)' in body(P,'protected void Investigate(')
 methods='\n'.join(body(P,s) for s in ('protected int Assigned(', 'protected void FinishWatch(', 'protected void LostOrder('))
 if 'protected float HuntCommitment(' in P.read_text(): methods=body(P,'protected float HuntCommitment(')+'\n'+methods
+if 'protected void ReconsiderHunt(' in P.read_text(): methods=body(P,'protected void ReconsiderHunt(')+'\n'+methods
+if 'protected void RememberCompletedHunt(' in P.read_text(): methods=body(P,'protected void RememberCompletedHunt(')+'\n'+methods
 unit_methods='\n'.join(body(P,s) for s in ('void CancelPending(', 'ERR_HQTask PlannedTask('))
 rules='\n'.join(body(R,s) for s in ('static bool Finite(', 'static bool PositionValid(', 'static bool BearingAllowed('))
 coverage_rules='\n'.join(body(C,s) for s in ('static bool PositionValid(', 'static int Cell(', 'static int Key(', 'static bool Point('))
@@ -70,7 +72,7 @@
  ERR_HQReport* Assignment=nullptr;ERR_HQReport* PendingOrder=nullptr;ERR_HQRoute* Downlink=nullptr;
  void* PendingConvoyOrder=nullptr;bool PendingCheckin=false;void* Waypoint=nullptr;
 '''+cpp(unit_methods)+r'''};
-struct ERR_HQHuntWindow {float NextHunt=900000;};
+struct ERR_HQHuntWindow {float CompletedUntil=0;float NextHunt=900000;};
 struct Watch {void FinishInvestigation(std::string,int,int,float){}};
 struct HQ {std::vector<ERR_HQUnit*>m_Units;ERR_HQHuntWindow window;Watch m_Watch;
  ERR_HQHuntWindow* Hunt(int){return &window;}
