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,15 @@
Finite designated zero-stock mine replacement proposal
5 production files, 6 existing regression programs; no new registration.
Read-only predecessor evidence: escapists-ai/Scripts/Game/EscapistsAI/EAI_Mines.c Inspect calls Issue then requires EAI_MinesIssued, requests STOCK-count at count0 or1. Successor preserves physical finite convoy stock and only one transfer/trip.
Role: m_Designated latches only successful original chance roll. ReadyForResupply(replacement=true) requires designation AND terminal settled initial callback. Later settings cannot confer/revoke role. MineSupplyReady defaults preserve existing one-carrier path. A false native submission with unfinished callback remains quarantined deliberately (no new refusal interpretation).
Planning: complete delivered count0/1, max120s, shared replay/cooldown; zero requires settled designated original attributable member before claims/orders. No extra live inventory/position query in planning.
Physical: explicit bounded CarriedCount (-1 unknown), pin zero/one when original receiving member selected, same native access/exact finite reservation. Zero leaf independently revalidates current command HQ role and zero inventory excluding incoming only after submitted. No change to uncertain callback/deletion release semantics or state7 retained cross-truck claims.
EngineerMineAttempted and mine placement policy are untouched: this replenishes carried capability, not repeated laying authorization.
Evidence: six *.green.log files; baseline-planning.red.log compiles actual old TryPlan then assertion-fails valid designated zero admission. git apply --check passed. Native compile/physical transfer still parent qualification; no agent native run.
Generic test execution uses TMPDIR=.build/mine-zero-proposal/scratch and PYTHONPATH=target/tests; no canonical native /tmp guard changed. Source override variables ERR_LOADOUT_SOURCE, ERR_EQUIPMENT_SOURCE, ERR_MINE_TRANSFER_SOURCE, ERR_INFANTRY_RESUPPLY_SOURCE, ERR_HQ_SOURCE point to respective proposal files.
@@ -0,0 +1,481 @@
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c
@@ -154,6 +154,7 @@
}
class ERR_EnemyMineGrant
{
+ protected bool m_Designated;
protected bool m_Rolled;
protected bool m_Finished;
protected int m_Checks;
@@ -161,7 +162,7 @@
protected ref ERR_EnemyEquipmentCallback m_Operation;
// A terminal grant may still retain an accepted native insertion after timeout.
// Resupply cannot race that callback or restart the initial allowance.
- bool ReadyForResupply() { return m_Finished && (!m_Operation || m_Operation.Finished); }
+ bool ReadyForResupply(bool replacement = false) { return m_Finished && (!m_Operation || m_Operation.Finished) && (!replacement || m_Designated); }
void Check(ERR_GameMode mode, SCR_AIGroup group, IEntity actor)
{
if (m_Finished) return;
@@ -170,6 +171,7 @@
{
m_Rolled = true;
if (!ERR_EnemyLoadoutRules.Selected(mode.Settings().Runtime("antiPersonnelMineChancePercent"), Math.RandomInt(0, 100))) { m_Finished = true; return; }
+ m_Designated = true; // Original startup selection persists; it never grants another startup allowance.
m_Remaining = 2;
}
if (++m_Checks > 10) { m_Finished = true; ERR_HQ.Event("MINE EQUIPMENT", "startup deadline; pending native operation will not be duplicated"); return; }
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyEquipment.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyEquipment.c
@@ -141,12 +141,12 @@
}
foreach (ERR_EnemyEquipmentEntry entry : m_Entries) if (entry.Group == group) entry.Stop(m_Mode);
}
- bool MineSupplyReady(SCR_AIGroup group, IEntity actor)
+ bool MineSupplyReady(SCR_AIGroup group, IEntity actor, bool replacement = false)
{
if (m_Stopped || m_Entries.Count() > 2048 || !ERR_EnemyEquipmentRules.Owned(m_Mode, group, actor)) return false;
foreach (ERR_EnemyEquipmentEntry entry : m_Entries)
if (entry && entry.Actor == actor && entry.Group == group)
- return !entry.Released && entry.Mines && entry.Mines.ReadyForResupply();
+ return !entry.Released && entry.Mines && entry.Mines.ReadyForResupply(replacement);
return false;
}
protected bool Known(IEntity actor)
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c
@@ -347,9 +347,9 @@
foreach (ERR_HQUnit unit : m_Units) if (unit.Group == group) return unit.LogisticsReservation;
return 0;
}
- bool MineSupplyReady(SCR_AIGroup group, IEntity actor)
- {
- return Replication.IsServer() && MissionActive() && m_Equipment && m_Equipment.MineSupplyReady(group, actor);
+ bool MineSupplyReady(SCR_AIGroup group, IEntity actor, bool replacement = false)
+ {
+ return Replication.IsServer() && MissionActive() && m_Equipment && m_Equipment.MineSupplyReady(group, actor, replacement);
}
bool ReserveLogistics(SCR_AIGroup group, int token)
{
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c
@@ -19,7 +19,7 @@
return Fresh(recipient, now) && Fresh(donor, now) && recipient.SourceId != donor.SourceId
&& recipient.AmmoState && recipient.AmmoState.Valid(recipient.Count)
&& recipient.AmmoState.MineComplete && recipient.AmmoState.MineMembers == recipient.Count
- && recipient.AmmoState.ReadyMines == 1 && vector.DistanceXZ(recipient.Position, donor.Position) <= 200;
+ && recipient.AmmoState.ReadyMines <= 1 && vector.DistanceXZ(recipient.Position, donor.Position) <= 200;
}
static bool Pair(ERR_HQWatchEntry recipient, ERR_HQWatchEntry donor, float now)
{
@@ -44,6 +44,7 @@
protected bool m_MineStockCaptured;
protected bool m_MineTrip;
protected int m_MineRecipientSlot = -1;
+ protected bool m_EmptyMineReplacement;
protected int m_MinesDelivered;
protected ref array<IEntity> m_Members = {};
protected ref array<IEntity> m_Served = {};
@@ -208,6 +209,19 @@
if (mineTrip)
{
if (!ERR_InfantryResupplyRules.MinePair(demand, donorStatus, now)) return false;
+ // Empty reports authorize only an original startup-designated engineer.
+ // This reads retained entitlement, never live positions/inventory for planning.
+ if (demand.AmmoState.ReadyMines == 0)
+ {
+ bool designated;
+ for (int member = 0; member < recipient.StatusSampler.InitialStrength() && member < 32; member++)
+ {
+ IEntity original = recipient.StatusSampler.InitialMember(member);
+ if (original && recipient.StatusSampler.CanAttribute(member, original)
+ && m_HQ.MineSupplyReady(recipient.Group, original, true)) { designated = true; break; }
+ }
+ if (!designated) return false;
+ }
}
else if (!ERR_InfantryResupplyRules.Pair(demand, donorStatus, now)) return false;
if (demand.SourceId != recipient.ForceSourceId || donorStatus.SourceId != donor.ForceSourceId) return false;
@@ -235,7 +249,7 @@
if (slot < 0) slot = 0; // Isolated legacy fixture uses the first convoy's range.
int token = 100000 + slot * 128 + m_Trip + 1;
if (!m_HQ.ReserveLogistics(recipient.Group, token)) { m_Recipient = null; m_Donor = null; return false; }
- m_MineTrip = mineTrip; m_MineRecipientSlot = -1; m_Receiving = null;
+ m_MineTrip = mineTrip; m_MineRecipientSlot = -1; m_Receiving = null; m_EmptyMineReplacement = false;
m_Trip++; m_Token = token; m_Truck.SupplyToken = token; m_ReportId = demand.MessageId; m_TripDelivered = 0; m_Served.Clear();
m_Consumed.Set(demand.SourceId, demand.MessageId); m_NextAttempt.Set(demand.SourceId, now + 180000);
m_Deadline = Math.Min(now + 120000, demand.ObservedAt + 180000); m_State = 2;
@@ -325,22 +339,27 @@
if (!actor || !m_Members.Contains(actor) || !m_Recipient.StatusSampler.CanAttribute(slot, actor, false)) continue;
if (!ERR_Actors.CanFight(actor) || vector.DistanceXZ(actor.GetOrigin(), m_Truck.Entity.GetOrigin()) > 4) continue;
if (!m_HQ.MineSupplyReady(m_Recipient.Group, actor)) continue;
- if (!ERR_InfantryMineTransfer.Carrier(actor, m_Recipient.Group)) continue;
+ IEntity carried;
+ int count = ERR_InfantryMineTransfer.CarriedCount(actor, m_Recipient.Group, carried);
+ if (count != 1 && (count != 0 || !m_HQ.MineSupplyReady(m_Recipient.Group, actor, true))) continue;
+ m_EmptyMineReplacement = count == 0;
m_Receiving = actor; m_MineRecipientSlot = slot; break;
}
if (!m_Receiving) return;
}
- if (!m_HQ.MineSupplyReady(m_Recipient.Group, m_Receiving)) return;
+ if (!m_HQ.MineSupplyReady(m_Recipient.Group, m_Receiving, m_EmptyMineReplacement)) return;
+ IEntity carried;
+ int count = ERR_InfantryMineTransfer.CarriedCount(m_Receiving, m_Recipient.Group, carried);
if (!m_Recipient.StatusSampler.CanAttribute(m_MineRecipientSlot, m_Receiving, false)
- || !ERR_InfantryMineTransfer.Carrier(m_Receiving, m_Recipient.Group))
- { Finish("original one-mine carrier lost or no longer has exactly one usable mine"); return; }
+ || (m_EmptyMineReplacement && count != 0) || (!m_EmptyMineReplacement && count != 1))
+ { Finish("original mine recipient lost or its pinned zero/one stock changed"); return; }
foreach (IEntity mine : m_MineStock)
{
if (!ERR_InfantryMineTransfer.ReadyMine(mine) || m_Spent.Contains(mine) || !Contains(m_Truck.Entity, mine)) continue;
ERR_FiniteMagazineReservation reservation = new ERR_FiniteMagazineReservation(m_MineStock, m_Spent, mine);
if (!reservation.Reserve()) continue;
m_MineMove = new ERR_InfantryMineTransfer(m_Mode, m_Truck.Entity, m_Truck.Group, m_Receiving, m_Recipient.Group,
- mine, reservation, m_Recipient.StatusSampler, m_MineRecipientSlot);
+ mine, reservation, m_Recipient.StatusSampler, m_MineRecipientSlot, m_EmptyMineReplacement);
m_MineMove.Tick(now);
// The leaf commits before the one native move. Only an unsubmitted refusal
// may release its tentative claim while the same carrier approaches/retries.
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryMineTransfer.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryMineTransfer.c
@@ -7,7 +7,7 @@
protected override void OnAllFailed() { if (Transfer) Transfer.Acknowledge(false); }
}
-// One finite original PMN4 tops up an existing original carrier from one to two.
+// One finite original PMN4 tops up a carrier, or replaces an original designated engineer's empty stock.
// The coordinator owns radio authorization and the shared truck/recipient claim.
class ERR_InfantryMineTransfer
{
@@ -18,6 +18,7 @@
protected SCR_AIGroup m_RecipientGroup;
protected IEntity m_Mine;
protected IEntity m_CarriedMine;
+ protected bool m_EmptyReplacement;
protected IEntity m_Driver;
protected ref ERR_ForceRecord m_DonorRecord;
protected ref ERR_ForceRecord m_RecipientRecord;
@@ -38,11 +39,11 @@
protected ERR_InfantryAmmoTransferState m_State = ERR_InfantryAmmoTransferState.PENDING;
protected string m_Reason;
- void ERR_InfantryMineTransfer(ERR_GameMode mode, Vehicle donorVehicle, SCR_AIGroup donorGroup, IEntity recipientActor, SCR_AIGroup recipientGroup, IEntity exactMine, ERR_FiniteMagazineReservation reservation, ERR_HQStatusSampler originalRoster, int originalSlot)
+ void ERR_InfantryMineTransfer(ERR_GameMode mode, Vehicle donorVehicle, SCR_AIGroup donorGroup, IEntity recipientActor, SCR_AIGroup recipientGroup, IEntity exactMine, ERR_FiniteMagazineReservation reservation, ERR_HQStatusSampler originalRoster, int originalSlot, bool emptyReplacement = false)
{
m_Mode = mode; m_Donor = donorVehicle; m_DonorGroup = donorGroup;
m_Recipient = recipientActor; m_RecipientGroup = recipientGroup; m_Mine = exactMine;
- m_Reservation = reservation; m_Roster = originalRoster; m_Slot = originalSlot;
+ m_Reservation = reservation; m_Roster = originalRoster; m_Slot = originalSlot; m_EmptyReplacement = emptyReplacement;
if (!mode || !mode.Forces() || !donorVehicle || !recipientActor || !originalRoster) return;
m_DonorRecord = mode.Forces().FindGroup(donorGroup); m_RecipientRecord = mode.Forces().FindGroup(recipientGroup);
m_Strength = originalRoster.InitialStrength();
@@ -65,31 +66,38 @@
}
// Exclusion is used only after submission: the incoming item may already be
// carried, but the exact pre-existing one must still prove carrier entitlement.
- static IEntity Carrier(IEntity actor, SCR_AIGroup group, IEntity exclude = null)
- {
- if (!Replication.IsServer() || !group || group.IsSlave() || group.GetPlayerCount(true) > 0) return null;
+ static int CarriedCount(IEntity actor, SCR_AIGroup group, out IEntity found, IEntity exclude = null)
+ {
+ found = null;
+ if (!Replication.IsServer() || !group || group.IsSlave() || group.GetPlayerCount(true) > 0) return -1;
if (!ERR_Actors.CanFight(actor) || ERR_Actors.Player(actor) || ERR_Actors.Group(actor) != group
- || CompartmentAccessComponent.GetVehicleIn(actor)) return null;
+ || CompartmentAccessComponent.GetVehicleIn(actor)) return -1;
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent));
- if (!inventory || inventory.GetOwner() != actor) return null;
+ if (!inventory || inventory.GetOwner() != actor) return -1;
array<IEntity> items = {}; inventory.GetItems(items);
- if (items.Count() > 128) return null;
- array<IEntity> seen = {}; IEntity found;
+ if (items.Count() > 128) return -1;
+ array<IEntity> seen = {}; int count;
foreach (IEntity item : items)
{
- if (!item || !item.GetPrefabData()) return null;
+ if (!item || !item.GetPrefabData()) return -1;
if (seen.Contains(item)) continue;
seen.Insert(item);
if (item == exclude || item.GetPrefabData().GetPrefabName() != ERR_EnemyLoadoutCatalog.MINE) continue;
InventoryItemComponent carried = InventoryItemComponent.Cast(item.FindComponent(InventoryItemComponent));
if (!carried || !carried.GetParentSlot() || carried.GetParentSlot().GetAttachedEntity() != item
- || !ERR_InfantryAmmoTransfer.StoredUnder(item, actor)) return null;
+ || !ERR_InfantryAmmoTransfer.StoredUnder(item, actor)) return -1;
if (!item.FindComponent(SCR_AntiPersonnelMineCollisionHandlerComponent)
- || !item.FindComponent(SCR_PressureTriggerComponent) || !item.FindComponent(DamageManagerComponent)) return null;
+ || !item.FindComponent(SCR_PressureTriggerComponent) || !item.FindComponent(DamageManagerComponent)) return -1;
if (!ReadyMine(item)) continue;
- if (found) return null;
+ count++;
found = item;
}
+ return count;
+ }
+ static IEntity Carrier(IEntity actor, SCR_AIGroup group, IEntity exclude = null)
+ {
+ IEntity found;
+ if (CarriedCount(actor, group, found, exclude) != 1) return null;
return found;
}
bool Submitted() { return m_Submitted; }
@@ -177,10 +185,17 @@
if (!m_Source || !m_Destination || m_Source == m_Destination) return false;
if (m_Donor.FindComponent(InventoryStorageManagerComponent) != m_Source
|| m_Recipient.FindComponent(InventoryStorageManagerComponent) != m_Destination) return false;
- if (!m_CarriedMine || !ReadyMine(m_Mine)) return false;
+ if (!ReadyMine(m_Mine)) return false;
IEntity exclude;
if (m_Submitted) exclude = m_Mine;
- if (Carrier(m_Recipient, m_RecipientGroup, exclude) != m_CarriedMine) return false;
+ if (m_EmptyReplacement)
+ {
+ ERR_HQ hq = ERR_HQ.Cast(m_Mode.Command());
+ if (!hq || !hq.MineSupplyReady(m_RecipientGroup, m_Recipient, true)) return false;
+ IEntity found;
+ if (CarriedCount(m_Recipient, m_RecipientGroup, found, exclude) != 0) return false;
+ }
+ else if (!m_CarriedMine || Carrier(m_Recipient, m_RecipientGroup, exclude) != m_CarriedMine) return false;
Physics physics = m_Donor.GetPhysics();
if (!physics) return false;
float speed = physics.GetVelocity().Length(); float angular = physics.GetAngularVelocity().Length();
--- a/tests/test_enemy_loadout.py
+++ b/tests/test_enemy_loadout.py
@@ -1,7 +1,8 @@
"""Execute production finite launcher/fallback/mine controllers with native boundaries."""
from policy_harness import ROOT, run, body
-import re
-source=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c'
+import re,os
+from pathlib import Path
+source=Path(os.environ.get('ERR_LOADOUT_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c'))
s=source.read_text();s=s[s.index('class ERR_EnemyLauncherGrant'):]
s=re.sub(r'//[^\n]*','',s).replace('protected ','').replace('ref ','').replace('class ','struct ')
s=s.replace('ResourceName','string').replace('array<IEntity>','Array<IEntity*>')
@@ -61,8 +62,9 @@
assert(!ERR_EnemyLoadoutRules::Selected(-1,0)&&!ERR_EnemyLoadoutRules::Selected(101,0));
ERR_GameMode mode;Math::roll=99;
Soldier off;ERR_EnemyLauncherGrant noaa;ERR_EnemyMineGrant nomines;mode.settings.aa=mode.settings.mines=0;
- noaa.Check(&mode,&off.group,&off.actor);nomines.Check(&mode,&off.group,&off.actor);assert(off.inv.requests==0);
+ noaa.Check(&mode,&off.group,&off.actor);nomines.Check(&mode,&off.group,&off.actor);assert(off.inv.requests==0);assert(nomines.ReadyForResupply()&&!nomines.ReadyForResupply(true));
mode.settings.aa=mode.settings.mines=100;
+ nomines.Check(&mode,&off.group,&off.actor);assert(!nomines.ReadyForResupply(true)&&off.inv.requests==0);
Soldier good;ERR_EnemyLauncherGrant aa;aa.Check(&mode,&good.group,&good.actor);assert(good.inv.last=="IGLA");
IEntity igla;igla.prefab="IGLA";good.Add(igla,true);good.inv.Complete(true);aa.Check(&mode,&good.group,&good.actor);assert(aa.m_Finished&&good.inv.requests==1);
good.mgr.weapons.clear();good.inv.items.clear();for(int n=0;n<20;n++)aa.Check(&mode,&good.group,&good.actor);assert(good.inv.requests==1);
@@ -87,12 +89,14 @@
miner.inv.items.clear();for(int n=0;n<20;n++)mines.Check(&mode,&miner.group,&miner.actor);assert(miner.inv.requests==2);
Soldier full;ERR_EnemyMineGrant existing;full.Add(mine1,false);full.Add(mine2,false);existing.Check(&mode,&full.group,&full.actor);assert(existing.m_Finished&&full.inv.requests==0);
Soldier dropped;ERR_EnemyMineGrant finite;finite.Check(&mode,&dropped.group,&dropped.actor);dropped.inv.Complete(true);finite.Check(&mode,&dropped.group,&dropped.actor);dropped.inv.Complete(true);finite.Check(&mode,&dropped.group,&dropped.actor);assert(finite.m_Finished&&dropped.inv.requests==2);
- Soldier noSpace;noSpace.inv.accept=false;ERR_EnemyMineGrant noLoose;noLoose.Check(&mode,&noSpace.group,&noSpace.actor);assert(noLoose.m_Finished&&noSpace.inv.requests==1);
+ Soldier noSpace;noSpace.inv.accept=false;ERR_EnemyMineGrant noLoose;noLoose.Check(&mode,&noSpace.group,&noSpace.actor);assert(noLoose.m_Finished&&noSpace.inv.requests==1&&!noLoose.ReadyForResupply(true));
+ noSpace.inv.Complete(false);assert(noLoose.ReadyForResupply(true)); // Finished refusal settles uncertainty; no extra startup request.
Soldier timeoutActor;ERR_EnemyMineGrant timeoutGrant;
timeoutGrant.Check(&mode,&timeoutActor.group,&timeoutActor.actor);
for(int i=0;i<12;i++)timeoutGrant.Check(&mode,&timeoutActor.group,&timeoutActor.actor);
- assert(timeoutGrant.m_Finished&&!timeoutGrant.ReadyForResupply()&&timeoutActor.inv.requests==1);
- timeoutActor.inv.Complete(true);assert(timeoutGrant.ReadyForResupply());
+ assert(timeoutGrant.m_Finished&&!timeoutGrant.ReadyForResupply()&&!timeoutGrant.ReadyForResupply(true)&&timeoutActor.inv.requests==1);
+ timeoutActor.inv.Complete(true);assert(timeoutGrant.ReadyForResupply()&&timeoutGrant.ReadyForResupply(true));
+ mode.settings.mines=0;assert(timeoutGrant.ReadyForResupply(true)); // Current setting cannot confer or revoke original designation.
timeoutGrant.Check(&mode,&timeoutActor.group,&timeoutActor.actor);assert(timeoutActor.inv.requests==1);
}
--- a/tests/test_enemy_equipment.py
+++ b/tests/test_enemy_equipment.py
@@ -36,7 +36,7 @@
void Check(ERR_GameMode*){checks++;}void Stop(){stops++;}
};
struct ERR_EnemyRadio{ERR_EnemyRadio(SCR_AIGroup*,int){}void Register(IEntity*){}void Check(ERR_GameMode*){}void Stop(){}};
-struct MineGrant{bool settled=false;bool ReadyForResupply(){return settled;}};
+struct MineGrant{bool settled=false,designated=false;bool ReadyForResupply(bool replacement=false){return settled&&(!replacement||designated);}};
struct ERR_EnemyEquipmentEntry{
MineGrant* Mines=nullptr;
ERR_EnemyOptics* Optics=nullptr;bool Complete=false;
@@ -53,9 +53,9 @@
assert(entry->Complete&&entry->Optics->checks==1&&combat.refreshes==1); // Stinger is already complete; optics has its own startup completion.
MineGrant grant;entry->Mines=&grant;IEntity foreign;SCR_AIGroup otherGroup;
assert(!service.MineSupplyReady(&group,&soldier));grant.settled=true;
- assert(service.MineSupplyReady(&group,&soldier));assert(!service.MineSupplyReady(&otherGroup,&soldier));assert(!service.MineSupplyReady(&group,&foreign));
+ assert(service.MineSupplyReady(&group,&soldier));assert(!service.MineSupplyReady(&group,&soldier,true));grant.designated=true;assert(service.MineSupplyReady(&group,&soldier,true));assert(!service.MineSupplyReady(&otherGroup,&soldier,true));assert(!service.MineSupplyReady(&otherGroup,&soldier));assert(!service.MineSupplyReady(&group,&foreign));
soldier.eligible=false;assert(!service.MineSupplyReady(&group,&soldier));soldier.eligible=true;
- entry->Released=true;assert(!service.MineSupplyReady(&group,&soldier));entry->Released=false;
+ entry->Released=true;assert(!service.MineSupplyReady(&group,&soldier));assert(!service.MineSupplyReady(&group,&soldier,true));entry->Released=false;
entry->Mines=nullptr;assert(!service.MineSupplyReady(&group,&soldier));entry->Mines=&grant;
service.m_Stopped=true;assert(!service.MineSupplyReady(&group,&soldier));service.m_Stopped=false;
service.m_Entries.resize(2049,nullptr);assert(!service.MineSupplyReady(&group,&soldier));service.m_Entries.resize(1);
--- a/tests/test_infantry_mine_transfer.py
+++ b/tests/test_infantry_mine_transfer.py
@@ -12,12 +12,13 @@
stored=body(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c',' static bool StoredUnder(')
def cpp(s):
s=re.sub(r'//[^\n]*','',s).replace('protected ','').replace('override ','').replace('ref ','').replace('class ','struct ').replace('foreach (','for (').replace('array<','Array<').replace('null','nullptr')
+ s=s.replace('out IEntity found','IEntity& found').replace('ERR_HQ.Cast(m_Mode.Command())','m_Mode.Command()')
s=s.replace('void ERR_InfantryMineTransfer(', 'ERR_InfantryMineTransfer(').replace('void ERR_FiniteMagazineReservation(', 'ERR_FiniteMagazineReservation(')
for typ in ('InventoryStorageManagerComponent','InventoryItemComponent','SCR_AIVehicleUsageComponent','SCR_PressureTriggerComponent'):
s=re.sub(typ+r'\.Cast\((\w+)\.FindComponent\('+typ+r'\)\)',r'\1.'+{'InventoryStorageManagerComponent':'inventory','InventoryItemComponent':'item','SCR_AIVehicleUsageComponent':'usage','SCR_PressureTriggerComponent':'trigger'}[typ],s)
for typ,field in [('InventoryStorageManagerComponent','inventory'),('SCR_AntiPersonnelMineCollisionHandlerComponent','collision'),('SCR_PressureTriggerComponent','trigger'),('DamageManagerComponent','damage')]:
s=re.sub(r'(\w+)\.FindComponent\('+typ+r'\)',r'\1.'+field,s)
- for typ in ('ERR_GameMode','ERR_ForceRecord','ERR_HQStatusSampler','InventoryStorageManagerComponent','InventoryItemComponent','InventoryStorageSlot','BaseInventoryStorageComponent','SCR_AIVehicleUsageComponent','SCR_PressureTriggerComponent','SCR_AIGroupUtilityComponent','Physics','ERR_InfantryMineTransfer','ERR_InfantryMineCallback','ERR_FiniteMagazineReservation'):
+ for typ in ('ERR_HQ','ERR_GameMode','ERR_ForceRecord','ERR_HQStatusSampler','InventoryStorageManagerComponent','InventoryItemComponent','InventoryStorageSlot','BaseInventoryStorageComponent','SCR_AIVehicleUsageComponent','SCR_PressureTriggerComponent','SCR_AIGroupUtilityComponent','Physics','ERR_InfantryMineTransfer','ERR_InfantryMineCallback','ERR_FiniteMagazineReservation'):
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
s=s.replace('Array<BaseInventoryStorageComponent>','Array<BaseInventoryStorageComponent*>').replace('Array<IEntity> attempted','Array<IEntity>* attempted')
for field in ('m_Original','m_Spent'):
@@ -25,7 +26,7 @@
s=s.replace('Array<IEntity> original, Array<IEntity> spent','Array<IEntity>* original, Array<IEntity>* spent')
for ns in ('Replication','SCR_Global','ERR_GameMode','ERR_Actors','ERR_EnemyEquipmentRules','ERR_ConvoyRules','SCR_FactionManager','CompartmentAccessComponent','ERR_InfantryAmmoTransfer','ERR_InfantryAmmoAccess','ERR_ForceRules','ERR_EnemyLoadoutCatalog','ERR_InfantryAmmoTransferState','EStoragePurpose'):
s=s.replace(ns+'.',ns+'::')
- for var in ('m_Mine','exactMine','mode','donorVehicle','recipientActor','originalRoster','usage','item','actor','group','inventory','carried','trigger','m_Mode','m_Donor','m_DonorGroup','m_Recipient','m_RecipientGroup','m_Roster','m_DonorRecord','m_RecipientRecord','m_Source','m_Destination','m_Reservation','m_Callback','m_FromSlot','m_FromStorage','m_Storage','slot','storage','utility','physics','Transfer','entity'):
+ for var in ('hq','m_Mine','exactMine','mode','donorVehicle','recipientActor','originalRoster','usage','item','actor','group','inventory','carried','trigger','m_Mode','m_Donor','m_DonorGroup','m_Recipient','m_RecipientGroup','m_Roster','m_DonorRecord','m_RecipientRecord','m_Source','m_Destination','m_Reservation','m_Callback','m_FromSlot','m_FromStorage','m_Storage','slot','storage','utility','physics','Transfer','entity'):
s=re.sub(r'\b'+var+r'\.',var+'->',s)
for call in ('Forces()','GetPilotCompartmentSlot()','GetPrefabData()','GetParentSlot()'):
s=s.replace(call+'.',call+'->')
@@ -60,7 +61,8 @@
struct Group{bool slave=false;int players=0,faction=1;SCR_AIGroupUtilityComponent*utility=nullptr;bool IsSlave(){return slave;}int GetPlayerCount(bool){return players;}int GetFaction(){return faction;}SCR_AIGroupUtilityComponent*GetGroupUtilityComponent(){return utility;}};
struct ERR_ForceRecord{SCR_AIGroup Group=nullptr;bool Physical=true,Eliminated=false;};
struct Ledger{SCR_AIGroup donor=nullptr,recipient=nullptr;ERR_ForceRecord*d=nullptr,*r=nullptr;ERR_ForceRecord*FindGroup(SCR_AIGroup g){return g==donor?d:g==recipient?r:nullptr;}};
-struct ERR_GameMode{inline static ERR_GameMode*current=nullptr;bool active=true;Ledger*ledger=nullptr;static ERR_GameMode*Get(){return current;}bool Active(){return active;}Ledger*Forces(){return ledger;}int EnemyFaction(){return 1;}};
+struct ERR_HQ{bool designated=true;bool MineSupplyReady(SCR_AIGroup,IEntity,bool replacement=false){return !replacement||designated;}};
+struct ERR_GameMode{ERR_HQ hq;bool command=true;ERR_HQ*Command(){return command?&hq:nullptr;}inline static ERR_GameMode*current=nullptr;bool active=true;Ledger*ledger=nullptr;static ERR_GameMode*Get(){return current;}bool Active(){return active;}Ledger*Forces(){return ledger;}int EnemyFaction(){return 1;}};
struct ERR_HQStatusSampler{SCR_AIGroup group=nullptr;IEntity actor=nullptr;bool valid=true;int strength=1;bool OwnsRoster(SCR_AIGroup g){return g==group;}int InitialStrength(){return strength;}IEntity InitialMember(int i){return i==0?actor:nullptr;}bool CanAttribute(int i,IEntity a,bool){return valid&&i==0&&a==actor;}};
struct Replication{inline static bool server=true;static bool IsServer(){return server;}};
struct SCR_Global{inline static bool edit=false;static bool IsEditMode(){return edit;}};
@@ -95,10 +97,19 @@
from.storage=&sourceStorage;from.attached=&incoming;to.storage=&destStorage;ownSlot.storage=&destStorage;ownSlot.attached=&original;extraSlot.storage=&destStorage;extraSlot.attached=&extra;
incoming.prefab=&prefab;incoming.trigger=&inTrigger;incoming.item=&inItem;inItem.slot=&from;original.prefab=&prefab;original.trigger=&ownTrigger;original.item=&ownItem;ownItem.slot=&ownSlot;extra.prefab=&prefab;extra.trigger=&extraTrigger;extra.item=&extraItem;extraItem.slot=&extraSlot;
source.items={&incoming};destination.items={&original};roster.group=&recipientGroup;roster.actor=&actor;stock={&incoming};assert(reservation.Reserve());}
- ERR_InfantryMineTransfer Job(){return ERR_InfantryMineTransfer(&mode,&truck,&donorGroup,&actor,&recipientGroup,&incoming,&reservation,&roster,0);}
+ ERR_InfantryMineTransfer Job(bool empty=false){return ERR_InfantryMineTransfer(&mode,&truck,&donorGroup,&actor,&recipientGroup,&incoming,&reservation,&roster,0,empty);}
void Move(){source.items.clear();destination.items.Insert(&incoming);from.attached=nullptr;to.attached=&incoming;inItem.slot=&to;}
};
int main(){
+ // Authoritative designation plus an inspected empty inventory permits exactly one finite replacement.
+ {Fixture f;f.destination.items.clear();auto t=f.Job(true);assert(t.Tick(0)==PENDING&&t.Submitted());f.Move();f.source.last->OnComplete();assert(t.Tick(1)==SUCCEEDED&&f.destination.items.Count()==1&&f.source.calls==1);}
+ for(int k=0;k<7;k++){Fixture f;f.destination.items.clear();
+ if(k==0)f.mode.hq.designated=false;if(k==1)f.destination.items.Insert(nullptr);if(k==2)f.destination.items.Insert(&f.original);if(k==3)f.destination.owner=&f.extra;if(k==4)f.destination.items.resize(129,&f.original);if(k==5){f.original.damage=false;f.destination.items.Insert(&f.original);}if(k==6)f.mode.command=false;
+ auto t=f.Job(true);assert(t.Tick(0)==FAILED&&!t.Submitted()&&f.source.calls==0);}
+ // Empty/one mode never switches after dispatch; a foreign new mine or revoked entitlement aborts and retains uncertainty.
+ for(int k=0;k<2;k++){Fixture f;f.destination.items.clear();auto t=f.Job(true);t.Tick(0);if(k==0)f.destination.items.Insert(&f.original);if(k==1){f.mode.hq.designated=false;}assert(t.Tick(1)==FAILED&&t.Unresolved()&&f.source.calls==1);f.Move();f.source.last->OnComplete();assert(!t.Unresolved()&&t.Tick(2)==FAILED);}
+ {Fixture f;f.destination.items.clear();auto t=f.Job(true);t.Tick(0);t.Stop();f.Move();f.source.last->OnComplete();assert(!t.Unresolved()&&t.Tick(1)==FAILED&&f.source.calls==1&&!f.reservation.ReleaseUnsubmitted());}
+
{Fixture f;auto t=f.Job();assert(t.Tick(0)==PENDING&&t.Submitted()&&t.Unresolved()&&f.source.calls==1);assert(f.reservation.CommittedFor(&f.incoming)&&!f.reservation.ReleaseUnsubmitted());
f.source.last->OnComplete();assert(t.Tick(1)==PENDING&&t.Unresolved());f.Move();assert(t.Tick(2)==SUCCEEDED&&!t.Unresolved());assert(f.destination.items.Count()==2);t.Tick(3);assert(f.source.calls==1);}
// Partially constructed contexts never bind later or reach native mutation.
@@ -134,4 +145,4 @@
{Fixture f;auto t=f.Job();t.Tick(0);f.roster.valid=false;assert(t.Tick(1)==FAILED&&t.Unresolved());f.Move();f.source.last->OnComplete();assert(!t.Unresolved()&&t.Tick(2)==FAILED);}
}
'''.replace(';if(', ';\n if('))
-print('PASS: actual finite PMN4 one-to-two entitlement, pinned original ownership, shared commit, physical custody, callback quarantine/deletion and no retry')
+print('PASS: actual finite PMN4 designated-zero/one entitlement, pinned original ownership, shared commit, physical custody, callback quarantine/deletion and no retry')
--- a/tests/test_infantry_mine_coordinator.py
+++ b/tests/test_infantry_mine_coordinator.py
@@ -18,23 +18,24 @@
#include <cmath>
using string=std::string;
struct Vec{float x=0;static float DistanceXZ(Vec a,Vec b){return std::abs(a.x-b.x);}};
-struct Group{};struct Mode{};struct Entity{bool ready=false,fight=true,owned=true,grant=true;int mines=1;Vec pos;Vec GetOrigin(){return pos;}};using IEntity=Entity*;
+struct Group{};struct Mode{};struct Entity{bool ready=false,fight=true,owned=true,grant=true,designated=false;int mines=1;Vec pos;Vec GetOrigin(){return pos;}};using IEntity=Entity*;
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}void Insert(T v){this->push_back(v);}bool Contains(T v){return std::find(this->begin(),this->end(),v)!=this->end();}void RemoveItem(T v){this->erase(std::remove(this->begin(),this->end(),v),this->end());}};
'''+translated_reservation()+r'''
struct Sampler{Array<IEntity>original;int InitialStrength(){return original.Count();}IEntity InitialMember(int i){return original[i];}bool CanAttribute(int i,IEntity x,bool){return i>=0&&i<InitialStrength()&&original[i]==x&&x->owned;}};
struct Unit{struct Group*Group;Sampler*StatusSampler;};struct Truck{IEntity Entity;struct Group*Group;};
-struct HQ{bool MineSupplyReady(Group*,IEntity x){return x&&x->grant;}};
+struct HQ{bool MineSupplyReady(Group*,IEntity x,bool replacement=false){return x&&x->grant&&(!replacement||x->designated);}};
struct ERR_Actors{static bool CanFight(IEntity x){return x&&x->fight;}};
struct ERR_InfantryMineTransfer{
static inline bool accept=true;static inline int made=0;IEntity item,actor;ERR_FiniteMagazineReservation*r;bool submitted=false;int result=0;
- ERR_InfantryMineTransfer(Mode*,IEntity,Group*,IEntity a,Group*,IEntity i,ERR_FiniteMagazineReservation*q,Sampler*,int):item(i),actor(a),r(q){made++;}
+ ERR_InfantryMineTransfer(Mode*,IEntity,Group*,IEntity a,Group*,IEntity i,ERR_FiniteMagazineReservation*q,Sampler*,int,bool empty=false):item(i),actor(a),r(q){assert(a->mines==(empty?0:1));made++;}
static bool ReadyMine(IEntity x){return x&&x->ready;}
+ static int CarriedCount(IEntity x,Group*,IEntity& found){found=x;return x?x->mines:-1;}
static IEntity Carrier(IEntity x,Group*){return x&&x->mines==1?x:nullptr;}
int Tick(float){if(accept&&!submitted){assert(r->Commit());submitted=true;}return result;}
bool Submitted(){return submitted;}
};
struct Service{
- HQ*m_HQ;Mode*m_Mode;Unit*m_Recipient;Truck*m_Truck;IEntity m_Receiving=nullptr;int m_MineRecipientSlot=-1,m_MinesDelivered=0,finishes=0;ERR_InfantryMineTransfer*m_MineMove=nullptr;
+ HQ*m_HQ;Mode*m_Mode;Unit*m_Recipient;Truck*m_Truck;IEntity m_Receiving=nullptr;bool m_EmptyMineReplacement=false;int m_MineRecipientSlot=-1,m_MinesDelivered=0,finishes=0;ERR_InfantryMineTransfer*m_MineMove=nullptr;
Array<IEntity>m_Members,m_MineStock,m_Spent,cargo;
bool Contains(IEntity,IEntity x){return cargo.Contains(x);}void Finish(string){finishes++;}
'''+s+r'''
@@ -43,7 +44,11 @@
Fixture():sampler{{&actor,&other}},unit{&g,&sampler},donor{&truck,&d}{mine.ready=second.ready=true;s.m_HQ=&h;s.m_Mode=&mode;s.m_Recipient=&unit;s.m_Truck=&donor;s.m_Members={&actor,&other};s.m_MineStock={&mine,&second};s.cargo=s.m_MineStock;ERR_InfantryMineTransfer::accept=true;}
};
int main(){
- // Never grant mines to zero/two-mine members or to unsettled initial grants.
+ {Fixture f;f.actor.mines=0;f.actor.designated=true;f.s.TransferMine(0);assert(f.s.m_MineMove&&f.s.m_MineMove->actor==&f.actor&&f.s.m_EmptyMineReplacement&&f.s.m_Spent.Count()==1);}
+ {Fixture f;f.actor.mines=-1;f.actor.designated=true;f.other.mines=0;f.s.TransferMine(0);assert(!f.s.m_MineMove&&f.s.m_Spent.empty());}
+ {Fixture f;f.actor.mines=0;f.actor.designated=true;ERR_InfantryMineTransfer::accept=false;f.s.TransferMine(0);assert(f.s.m_Receiving==&f.actor&&f.s.m_EmptyMineReplacement);f.actor.mines=1;ERR_InfantryMineTransfer::accept=true;f.s.TransferMine(1);assert(!f.s.m_MineMove&&f.s.finishes==1&&f.s.m_Spent.empty());}
+
+ // Never grant mines to undesignated zero/two-mine members or to unsettled initial grants.
for(int refusal=0;refusal<6;refusal++){Fixture f;f.other.mines=0;
if(refusal==0)f.actor.mines=0;if(refusal==1)f.actor.mines=2;if(refusal==2)f.actor.grant=false;if(refusal==3)f.actor.owned=false;if(refusal==4)f.actor.pos.x=5;if(refusal==5)f.actor.fight=false;
f.s.TransferMine(0);assert(!f.s.m_MineMove&&f.s.m_Spent.empty()&&!f.s.m_Receiving);}
@@ -62,16 +67,16 @@
{Fixture f;f.s.TransferMine(0);auto*leaf=f.s.m_MineMove;leaf->result=-1;f.s.TransferMine(1000);assert(f.s.m_MineMove==leaf&&f.s.finishes==1&&f.s.m_Spent.Count()==1);}
}
'''.replace('if(refusal==0)f.actor.mines=0;if(refusal==1)f.actor.mines=2;if(refusal==2)f.actor.grant=false;if(refusal==3)f.actor.owned=false;if(refusal==4)f.actor.pos.x=5;if(refusal==5)f.actor.fight=false;', 'if(refusal==0){f.actor.mines=0;}if(refusal==1){f.actor.mines=2;}if(refusal==2){f.actor.grant=false;}if(refusal==3){f.actor.owned=false;}if(refusal==4){f.actor.pos.x=5;}if(refusal==5){f.actor.fight=false;}'))
-print('PASS: actual one-mine original carrier selection, settled startup, shared finite claims, no refund after submission')
+print('PASS: actual designated zero/one original carrier selection, settled startup, shared finite claims, no refund after submission')
h=Path(os.environ.get('ERR_HQ_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'))
w=body(h,'bool MineSupplyReady(').replace('SCR_AIGroup group','Group* group').replace('Replication.','Replication::').replace('m_Equipment.','m_Equipment->')
run(r'''
#include <cassert>
struct Group{};struct Item{};using IEntity=Item*;
-struct Equipment{bool ready=false;Group*group=nullptr;IEntity actor=nullptr;bool MineSupplyReady(Group*g,IEntity a){return ready&&g==group&&a==actor;}};
+struct Equipment{bool ready=false,designated=false;Group*group=nullptr;IEntity actor=nullptr;bool MineSupplyReady(Group*g,IEntity a,bool replacement=false){return ready&&g==group&&a==actor&&(!replacement||designated);}};
struct Replication{static inline bool server=true;static bool IsServer(){return server;}};
struct HQ{Equipment*m_Equipment=nullptr;bool active=true;bool MissionActive(){return active;}
'''+w+r'''};
-int main(){Group group,other;Item actor;Equipment e;e.group=&group;e.actor=&actor;HQ h;assert(!h.MineSupplyReady(&group,&actor));h.m_Equipment=&e;assert(!h.MineSupplyReady(&group,&actor));e.ready=true;assert(h.MineSupplyReady(&group,&actor));assert(!h.MineSupplyReady(&other,&actor));h.active=false;assert(!h.MineSupplyReady(&group,&actor));h.active=true;Replication::server=false;assert(!h.MineSupplyReady(&group,&actor));}
+int main(){Group group,other;Item actor;Equipment e;e.group=&group;e.actor=&actor;HQ h;assert(!h.MineSupplyReady(&group,&actor));h.m_Equipment=&e;assert(!h.MineSupplyReady(&group,&actor));e.ready=true;assert(h.MineSupplyReady(&group,&actor));assert(!h.MineSupplyReady(&group,&actor,true));e.designated=true;assert(h.MineSupplyReady(&group,&actor,true));assert(!h.MineSupplyReady(&other,&actor,true));assert(!h.MineSupplyReady(&other,&actor));h.active=false;assert(!h.MineSupplyReady(&group,&actor));h.active=true;Replication::server=false;assert(!h.MineSupplyReady(&group,&actor));}
''')
print('PASS: actual HQ startup grant readiness forwarding is authoritative and fail-closed')
--- a/tests/test_infantry_resupply_planning.py
+++ b/tests/test_infantry_resupply_planning.py
@@ -23,6 +23,7 @@
s=s.replace(name+'.',name+'->')
for name in ('Forces()','GetPrefabData()','Convoys()'):s=s.replace(name+'.',name+'->')
s=s.replace('ERR_SupplyConvoyTask* supply', 'Convoy* supply')
+ s=s.replace('bool designated;','bool designated=false;')
s=s.replace('bool available;','bool available=false;').replace('bool living;','bool living=false;')
return s
rules='\n'.join(cpp(body(P,x)) for x in ('static bool Fresh(', 'static bool Need(', 'static bool Pair(', 'static bool MinePair('))
@@ -85,10 +86,11 @@
struct ERR_InfantryMineTransfer{static bool ReadyMine(IEntity x){return x&&x->readyMine;}};
struct Route{};struct Leaf{bool unresolved=false;void Stop(){}bool Unresolved(){return unresolved;}};struct Mounted{bool busy=false;bool Busy(){return busy;}void Stop(){}};
struct HQ{Mode* m_Mode;Array<ERR_HQUnit>m_Units;Convoy* convoy=nullptr;ConvoyService convoys;ConvoyService* Convoys(){return &convoys;}Route route;bool active=true,routeAvailable=true;int downlinks=0;bool MissionActive(){return active;}Convoy* ConvoyTask(){return convoy;}
+ bool designated=false;bool MineSupplyReady(Group*,IEntity,bool replacement=false){return !replacement||designated;}
Route* Downlink(Group*,string,string,int){downlinks++;return routeAvailable?&route:nullptr;}
'''+reserve+r'''
};
-struct Service{HQ* m_HQ;Mode* m_Mode;Convoy* m_Convoy=nullptr;Truck* m_Truck=nullptr;Unit* m_Recipient=nullptr;Unit* m_Donor=nullptr;Leaf* m_Move=nullptr;Leaf*m_MineMove=nullptr;MineBatch*m_MineBatch=nullptr;bool m_MineTrip=false,m_MineStockCaptured=false;int m_MineRecipientSlot=-1;Mounted*m_Mounted=nullptr;Route* m_Order=nullptr;IEntity m_Receiving=nullptr;
+struct Service{HQ* m_HQ;Mode* m_Mode;Convoy* m_Convoy=nullptr;Truck* m_Truck=nullptr;Unit* m_Recipient=nullptr;Unit* m_Donor=nullptr;Leaf* m_Move=nullptr;Leaf*m_MineMove=nullptr;MineBatch*m_MineBatch=nullptr;bool m_MineTrip=false,m_MineStockCaptured=false,m_EmptyMineReplacement=false;int m_MineRecipientSlot=-1;Mounted*m_Mounted=nullptr;Route* m_Order=nullptr;IEntity m_Receiving=nullptr;
Array<IEntity>m_Stock,m_MineStock,m_Spent,m_Members,m_Served;Map<int>*m_Consumed=new Map<int>();Map<float>*m_NextAttempt=new Map<float>();int m_ConvoySlot=-1;float m_NextTick=0;int m_State=0,m_Trip=0,m_Token=0,m_ReportId=0,m_TripDelivered=0;float m_Deadline=0,m_StockDeadline=-1;bool m_Stopped=false;
Waypoint* m_MoveWaypoint=nullptr;Waypoint* m_HoldWaypoint=nullptr;string reason;bool Current(){return !m_Stopped&&Replication::IsServer()&&m_HQ->active;}void Audit(string why){reason=why;}
'''+methods+r'''
@@ -179,9 +181,10 @@
{Fixture f;for(int i=48;i<96;i++)f.inventory.items.Insert(&f.items[i]);f.items[0].prefab.name="NATO_556_STANAG";
f.items[96].magazine=false;f.inventory.items.Insert(&f.items[96]);f.s.Stock(0);
assert(!f.s.m_Stopped&&f.s.m_State==1&&f.s.m_Stock.Count()==96&&f.s.m_Stock.Contains(&f.items[0])&&!f.s.m_Stock.Contains(&f.items[96]));}
- // Mine demand is a detached known-one report, never a grant to zero or unknown.
+ // Detached known-zero demand also requires settled original designation; unknown never authorizes.
{Fixture f;MineBatch batch;f.convoy.supply=true;f.convoy.MineStock=&batch;f.items[48].readyMine=true;batch.items={&f.items[48]};f.inventory.items.Insert(&f.items[48]);f.s.Stock(0);
for(int n: {0,2,3}){f.ammo.ReadyMines=n;assert(!f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));}
+ f.ammo.ReadyMines=0;f.hq.designated=true;f.one.released=f.two.released=true;assert(!f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));f.one.released=f.two.released=false;assert(f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));f.s.Finish("reset");f.s.m_Consumed->data.clear();f.s.m_NextAttempt->data.clear();f.hq.designated=false;
f.ammo.ReadyMines=1;f.ammo.MineComplete=false;assert(!f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));f.ammo.MineComplete=true;
batch.complete=false;assert(!f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));batch.complete=true;
assert(f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));assert(f.s.m_MineTrip&&f.s.m_MineStock.Count()==1);}
--- a/tests/test_enemy_radio.py
+++ b/tests/test_enemy_radio.py
@@ -39,7 +39,7 @@
float worldTime=0;
struct SCR_AICombatComponent{void ERR_BindEnemySkill(ERR_GameMode*,Group*){} void ERR_RefreshEnemySkill(){}};
struct ERR_EnemyOptics{ERR_EnemyOptics(IEntity,Group*){}void Check(ERR_GameMode*){}};
-struct MineGrant{bool ReadyForResupply(){return false;}};
+struct MineGrant{bool ReadyForResupply(bool replacement=false){return false;}};
struct ERR_EnemyEquipmentEntry{MineGrant*Mines=nullptr;IEntity Actor=nullptr;struct Group*Group=nullptr;SCR_AICombatComponent*Combat=nullptr;ERR_EnemyOptics*Optics=nullptr;bool Released=false;int SkillChecks=0;
void Check(ERR_GameMode*){}void Stop(ERR_GameMode*){Released=true;}};
'''
@@ -0,0 +1,19 @@
check: /home/jakub/devel/escapistsrereintroduced/.build/mine-zero-proposal/scratch/err-policy-4ozh465r/check.cpp:395: int main(): Assertion `f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0)' failed.
Traceback (most recent call last):
File "/home/jakub/devel/escapistsrereintroduced/.build/mine-zero-proposal/tests/test_infantry_resupply_planning.py", line 33, in <module>
run(r'''
~~~^^^^^
#include <cassert>
^^^^^^^^^^^^^^^^^^
...<190 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 '['/home/jakub/devel/escapistsrereintroduced/.build/mine-zero-proposal/scratch/err-policy-4ozh465r/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,11 @@
addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c
addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyEquipment.c
addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c
addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c
addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryMineTransfer.c
tests/test_enemy_loadout.py
tests/test_enemy_equipment.py
tests/test_infantry_mine_transfer.py
tests/test_infantry_mine_coordinator.py
tests/test_infantry_resupply_planning.py
tests/test_enemy_radio.py
@@ -0,0 +1,481 @@
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c
@@ -154,6 +154,7 @@
}
class ERR_EnemyMineGrant
{
+ protected bool m_Designated;
protected bool m_Rolled;
protected bool m_Finished;
protected int m_Checks;
@@ -161,7 +162,7 @@
protected ref ERR_EnemyEquipmentCallback m_Operation;
// A terminal grant may still retain an accepted native insertion after timeout.
// Resupply cannot race that callback or restart the initial allowance.
- bool ReadyForResupply() { return m_Finished && (!m_Operation || m_Operation.Finished); }
+ bool ReadyForResupply(bool replacement = false) { return m_Finished && (!m_Operation || m_Operation.Finished) && (!replacement || m_Designated); }
void Check(ERR_GameMode mode, SCR_AIGroup group, IEntity actor)
{
if (m_Finished) return;
@@ -170,6 +171,7 @@
{
m_Rolled = true;
if (!ERR_EnemyLoadoutRules.Selected(mode.Settings().Runtime("antiPersonnelMineChancePercent"), Math.RandomInt(0, 100))) { m_Finished = true; return; }
+ m_Designated = true; // Original startup selection persists; it never grants another startup allowance.
m_Remaining = 2;
}
if (++m_Checks > 10) { m_Finished = true; ERR_HQ.Event("MINE EQUIPMENT", "startup deadline; pending native operation will not be duplicated"); return; }
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyEquipment.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyEquipment.c
@@ -141,12 +141,12 @@
}
foreach (ERR_EnemyEquipmentEntry entry : m_Entries) if (entry.Group == group) entry.Stop(m_Mode);
}
- bool MineSupplyReady(SCR_AIGroup group, IEntity actor)
+ bool MineSupplyReady(SCR_AIGroup group, IEntity actor, bool replacement = false)
{
if (m_Stopped || m_Entries.Count() > 2048 || !ERR_EnemyEquipmentRules.Owned(m_Mode, group, actor)) return false;
foreach (ERR_EnemyEquipmentEntry entry : m_Entries)
if (entry && entry.Actor == actor && entry.Group == group)
- return !entry.Released && entry.Mines && entry.Mines.ReadyForResupply();
+ return !entry.Released && entry.Mines && entry.Mines.ReadyForResupply(replacement);
return false;
}
protected bool Known(IEntity actor)
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c
@@ -347,9 +347,9 @@
foreach (ERR_HQUnit unit : m_Units) if (unit.Group == group) return unit.LogisticsReservation;
return 0;
}
- bool MineSupplyReady(SCR_AIGroup group, IEntity actor)
- {
- return Replication.IsServer() && MissionActive() && m_Equipment && m_Equipment.MineSupplyReady(group, actor);
+ bool MineSupplyReady(SCR_AIGroup group, IEntity actor, bool replacement = false)
+ {
+ return Replication.IsServer() && MissionActive() && m_Equipment && m_Equipment.MineSupplyReady(group, actor, replacement);
}
bool ReserveLogistics(SCR_AIGroup group, int token)
{
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c
@@ -19,7 +19,7 @@
return Fresh(recipient, now) && Fresh(donor, now) && recipient.SourceId != donor.SourceId
&& recipient.AmmoState && recipient.AmmoState.Valid(recipient.Count)
&& recipient.AmmoState.MineComplete && recipient.AmmoState.MineMembers == recipient.Count
- && recipient.AmmoState.ReadyMines == 1 && vector.DistanceXZ(recipient.Position, donor.Position) <= 200;
+ && recipient.AmmoState.ReadyMines <= 1 && vector.DistanceXZ(recipient.Position, donor.Position) <= 200;
}
static bool Pair(ERR_HQWatchEntry recipient, ERR_HQWatchEntry donor, float now)
{
@@ -44,6 +44,7 @@
protected bool m_MineStockCaptured;
protected bool m_MineTrip;
protected int m_MineRecipientSlot = -1;
+ protected bool m_EmptyMineReplacement;
protected int m_MinesDelivered;
protected ref array<IEntity> m_Members = {};
protected ref array<IEntity> m_Served = {};
@@ -208,6 +209,19 @@
if (mineTrip)
{
if (!ERR_InfantryResupplyRules.MinePair(demand, donorStatus, now)) return false;
+ // Empty reports authorize only an original startup-designated engineer.
+ // This reads retained entitlement, never live positions/inventory for planning.
+ if (demand.AmmoState.ReadyMines == 0)
+ {
+ bool designated;
+ for (int member = 0; member < recipient.StatusSampler.InitialStrength() && member < 32; member++)
+ {
+ IEntity original = recipient.StatusSampler.InitialMember(member);
+ if (original && recipient.StatusSampler.CanAttribute(member, original)
+ && m_HQ.MineSupplyReady(recipient.Group, original, true)) { designated = true; break; }
+ }
+ if (!designated) return false;
+ }
}
else if (!ERR_InfantryResupplyRules.Pair(demand, donorStatus, now)) return false;
if (demand.SourceId != recipient.ForceSourceId || donorStatus.SourceId != donor.ForceSourceId) return false;
@@ -235,7 +249,7 @@
if (slot < 0) slot = 0; // Isolated legacy fixture uses the first convoy's range.
int token = 100000 + slot * 128 + m_Trip + 1;
if (!m_HQ.ReserveLogistics(recipient.Group, token)) { m_Recipient = null; m_Donor = null; return false; }
- m_MineTrip = mineTrip; m_MineRecipientSlot = -1; m_Receiving = null;
+ m_MineTrip = mineTrip; m_MineRecipientSlot = -1; m_Receiving = null; m_EmptyMineReplacement = false;
m_Trip++; m_Token = token; m_Truck.SupplyToken = token; m_ReportId = demand.MessageId; m_TripDelivered = 0; m_Served.Clear();
m_Consumed.Set(demand.SourceId, demand.MessageId); m_NextAttempt.Set(demand.SourceId, now + 180000);
m_Deadline = Math.Min(now + 120000, demand.ObservedAt + 180000); m_State = 2;
@@ -325,22 +339,27 @@
if (!actor || !m_Members.Contains(actor) || !m_Recipient.StatusSampler.CanAttribute(slot, actor, false)) continue;
if (!ERR_Actors.CanFight(actor) || vector.DistanceXZ(actor.GetOrigin(), m_Truck.Entity.GetOrigin()) > 4) continue;
if (!m_HQ.MineSupplyReady(m_Recipient.Group, actor)) continue;
- if (!ERR_InfantryMineTransfer.Carrier(actor, m_Recipient.Group)) continue;
+ IEntity carried;
+ int count = ERR_InfantryMineTransfer.CarriedCount(actor, m_Recipient.Group, carried);
+ if (count != 1 && (count != 0 || !m_HQ.MineSupplyReady(m_Recipient.Group, actor, true))) continue;
+ m_EmptyMineReplacement = count == 0;
m_Receiving = actor; m_MineRecipientSlot = slot; break;
}
if (!m_Receiving) return;
}
- if (!m_HQ.MineSupplyReady(m_Recipient.Group, m_Receiving)) return;
+ if (!m_HQ.MineSupplyReady(m_Recipient.Group, m_Receiving, m_EmptyMineReplacement)) return;
+ IEntity carried;
+ int count = ERR_InfantryMineTransfer.CarriedCount(m_Receiving, m_Recipient.Group, carried);
if (!m_Recipient.StatusSampler.CanAttribute(m_MineRecipientSlot, m_Receiving, false)
- || !ERR_InfantryMineTransfer.Carrier(m_Receiving, m_Recipient.Group))
- { Finish("original one-mine carrier lost or no longer has exactly one usable mine"); return; }
+ || (m_EmptyMineReplacement && count != 0) || (!m_EmptyMineReplacement && count != 1))
+ { Finish("original mine recipient lost or its pinned zero/one stock changed"); return; }
foreach (IEntity mine : m_MineStock)
{
if (!ERR_InfantryMineTransfer.ReadyMine(mine) || m_Spent.Contains(mine) || !Contains(m_Truck.Entity, mine)) continue;
ERR_FiniteMagazineReservation reservation = new ERR_FiniteMagazineReservation(m_MineStock, m_Spent, mine);
if (!reservation.Reserve()) continue;
m_MineMove = new ERR_InfantryMineTransfer(m_Mode, m_Truck.Entity, m_Truck.Group, m_Receiving, m_Recipient.Group,
- mine, reservation, m_Recipient.StatusSampler, m_MineRecipientSlot);
+ mine, reservation, m_Recipient.StatusSampler, m_MineRecipientSlot, m_EmptyMineReplacement);
m_MineMove.Tick(now);
// The leaf commits before the one native move. Only an unsubmitted refusal
// may release its tentative claim while the same carrier approaches/retries.
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryMineTransfer.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryMineTransfer.c
@@ -7,7 +7,7 @@
protected override void OnAllFailed() { if (Transfer) Transfer.Acknowledge(false); }
}
-// One finite original PMN4 tops up an existing original carrier from one to two.
+// One finite original PMN4 tops up a carrier, or replaces an original designated engineer's empty stock.
// The coordinator owns radio authorization and the shared truck/recipient claim.
class ERR_InfantryMineTransfer
{
@@ -18,6 +18,7 @@
protected SCR_AIGroup m_RecipientGroup;
protected IEntity m_Mine;
protected IEntity m_CarriedMine;
+ protected bool m_EmptyReplacement;
protected IEntity m_Driver;
protected ref ERR_ForceRecord m_DonorRecord;
protected ref ERR_ForceRecord m_RecipientRecord;
@@ -38,11 +39,11 @@
protected ERR_InfantryAmmoTransferState m_State = ERR_InfantryAmmoTransferState.PENDING;
protected string m_Reason;
- void ERR_InfantryMineTransfer(ERR_GameMode mode, Vehicle donorVehicle, SCR_AIGroup donorGroup, IEntity recipientActor, SCR_AIGroup recipientGroup, IEntity exactMine, ERR_FiniteMagazineReservation reservation, ERR_HQStatusSampler originalRoster, int originalSlot)
+ void ERR_InfantryMineTransfer(ERR_GameMode mode, Vehicle donorVehicle, SCR_AIGroup donorGroup, IEntity recipientActor, SCR_AIGroup recipientGroup, IEntity exactMine, ERR_FiniteMagazineReservation reservation, ERR_HQStatusSampler originalRoster, int originalSlot, bool emptyReplacement = false)
{
m_Mode = mode; m_Donor = donorVehicle; m_DonorGroup = donorGroup;
m_Recipient = recipientActor; m_RecipientGroup = recipientGroup; m_Mine = exactMine;
- m_Reservation = reservation; m_Roster = originalRoster; m_Slot = originalSlot;
+ m_Reservation = reservation; m_Roster = originalRoster; m_Slot = originalSlot; m_EmptyReplacement = emptyReplacement;
if (!mode || !mode.Forces() || !donorVehicle || !recipientActor || !originalRoster) return;
m_DonorRecord = mode.Forces().FindGroup(donorGroup); m_RecipientRecord = mode.Forces().FindGroup(recipientGroup);
m_Strength = originalRoster.InitialStrength();
@@ -65,31 +66,38 @@
}
// Exclusion is used only after submission: the incoming item may already be
// carried, but the exact pre-existing one must still prove carrier entitlement.
- static IEntity Carrier(IEntity actor, SCR_AIGroup group, IEntity exclude = null)
- {
- if (!Replication.IsServer() || !group || group.IsSlave() || group.GetPlayerCount(true) > 0) return null;
+ static int CarriedCount(IEntity actor, SCR_AIGroup group, out IEntity found, IEntity exclude = null)
+ {
+ found = null;
+ if (!Replication.IsServer() || !group || group.IsSlave() || group.GetPlayerCount(true) > 0) return -1;
if (!ERR_Actors.CanFight(actor) || ERR_Actors.Player(actor) || ERR_Actors.Group(actor) != group
- || CompartmentAccessComponent.GetVehicleIn(actor)) return null;
+ || CompartmentAccessComponent.GetVehicleIn(actor)) return -1;
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent));
- if (!inventory || inventory.GetOwner() != actor) return null;
+ if (!inventory || inventory.GetOwner() != actor) return -1;
array<IEntity> items = {}; inventory.GetItems(items);
- if (items.Count() > 128) return null;
- array<IEntity> seen = {}; IEntity found;
+ if (items.Count() > 128) return -1;
+ array<IEntity> seen = {}; int count;
foreach (IEntity item : items)
{
- if (!item || !item.GetPrefabData()) return null;
+ if (!item || !item.GetPrefabData()) return -1;
if (seen.Contains(item)) continue;
seen.Insert(item);
if (item == exclude || item.GetPrefabData().GetPrefabName() != ERR_EnemyLoadoutCatalog.MINE) continue;
InventoryItemComponent carried = InventoryItemComponent.Cast(item.FindComponent(InventoryItemComponent));
if (!carried || !carried.GetParentSlot() || carried.GetParentSlot().GetAttachedEntity() != item
- || !ERR_InfantryAmmoTransfer.StoredUnder(item, actor)) return null;
+ || !ERR_InfantryAmmoTransfer.StoredUnder(item, actor)) return -1;
if (!item.FindComponent(SCR_AntiPersonnelMineCollisionHandlerComponent)
- || !item.FindComponent(SCR_PressureTriggerComponent) || !item.FindComponent(DamageManagerComponent)) return null;
+ || !item.FindComponent(SCR_PressureTriggerComponent) || !item.FindComponent(DamageManagerComponent)) return -1;
if (!ReadyMine(item)) continue;
- if (found) return null;
+ count++;
found = item;
}
+ return count;
+ }
+ static IEntity Carrier(IEntity actor, SCR_AIGroup group, IEntity exclude = null)
+ {
+ IEntity found;
+ if (CarriedCount(actor, group, found, exclude) != 1) return null;
return found;
}
bool Submitted() { return m_Submitted; }
@@ -177,10 +185,17 @@
if (!m_Source || !m_Destination || m_Source == m_Destination) return false;
if (m_Donor.FindComponent(InventoryStorageManagerComponent) != m_Source
|| m_Recipient.FindComponent(InventoryStorageManagerComponent) != m_Destination) return false;
- if (!m_CarriedMine || !ReadyMine(m_Mine)) return false;
+ if (!ReadyMine(m_Mine)) return false;
IEntity exclude;
if (m_Submitted) exclude = m_Mine;
- if (Carrier(m_Recipient, m_RecipientGroup, exclude) != m_CarriedMine) return false;
+ if (m_EmptyReplacement)
+ {
+ ERR_HQ hq = ERR_HQ.Cast(m_Mode.Command());
+ if (!hq || !hq.MineSupplyReady(m_RecipientGroup, m_Recipient, true)) return false;
+ IEntity found;
+ if (CarriedCount(m_Recipient, m_RecipientGroup, found, exclude) != 0) return false;
+ }
+ else if (!m_CarriedMine || Carrier(m_Recipient, m_RecipientGroup, exclude) != m_CarriedMine) return false;
Physics physics = m_Donor.GetPhysics();
if (!physics) return false;
float speed = physics.GetVelocity().Length(); float angular = physics.GetAngularVelocity().Length();
--- a/tests/test_enemy_loadout.py
+++ b/tests/test_enemy_loadout.py
@@ -1,7 +1,8 @@
"""Execute production finite launcher/fallback/mine controllers with native boundaries."""
from policy_harness import ROOT, run, body
-import re
-source=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c'
+import re,os
+from pathlib import Path
+source=Path(os.environ.get('ERR_LOADOUT_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c'))
s=source.read_text();s=s[s.index('class ERR_EnemyLauncherGrant'):]
s=re.sub(r'//[^\n]*','',s).replace('protected ','').replace('ref ','').replace('class ','struct ')
s=s.replace('ResourceName','string').replace('array<IEntity>','Array<IEntity*>')
@@ -61,8 +62,9 @@
assert(!ERR_EnemyLoadoutRules::Selected(-1,0)&&!ERR_EnemyLoadoutRules::Selected(101,0));
ERR_GameMode mode;Math::roll=99;
Soldier off;ERR_EnemyLauncherGrant noaa;ERR_EnemyMineGrant nomines;mode.settings.aa=mode.settings.mines=0;
- noaa.Check(&mode,&off.group,&off.actor);nomines.Check(&mode,&off.group,&off.actor);assert(off.inv.requests==0);
+ noaa.Check(&mode,&off.group,&off.actor);nomines.Check(&mode,&off.group,&off.actor);assert(off.inv.requests==0);assert(nomines.ReadyForResupply()&&!nomines.ReadyForResupply(true));
mode.settings.aa=mode.settings.mines=100;
+ nomines.Check(&mode,&off.group,&off.actor);assert(!nomines.ReadyForResupply(true)&&off.inv.requests==0);
Soldier good;ERR_EnemyLauncherGrant aa;aa.Check(&mode,&good.group,&good.actor);assert(good.inv.last=="IGLA");
IEntity igla;igla.prefab="IGLA";good.Add(igla,true);good.inv.Complete(true);aa.Check(&mode,&good.group,&good.actor);assert(aa.m_Finished&&good.inv.requests==1);
good.mgr.weapons.clear();good.inv.items.clear();for(int n=0;n<20;n++)aa.Check(&mode,&good.group,&good.actor);assert(good.inv.requests==1);
@@ -87,12 +89,14 @@
miner.inv.items.clear();for(int n=0;n<20;n++)mines.Check(&mode,&miner.group,&miner.actor);assert(miner.inv.requests==2);
Soldier full;ERR_EnemyMineGrant existing;full.Add(mine1,false);full.Add(mine2,false);existing.Check(&mode,&full.group,&full.actor);assert(existing.m_Finished&&full.inv.requests==0);
Soldier dropped;ERR_EnemyMineGrant finite;finite.Check(&mode,&dropped.group,&dropped.actor);dropped.inv.Complete(true);finite.Check(&mode,&dropped.group,&dropped.actor);dropped.inv.Complete(true);finite.Check(&mode,&dropped.group,&dropped.actor);assert(finite.m_Finished&&dropped.inv.requests==2);
- Soldier noSpace;noSpace.inv.accept=false;ERR_EnemyMineGrant noLoose;noLoose.Check(&mode,&noSpace.group,&noSpace.actor);assert(noLoose.m_Finished&&noSpace.inv.requests==1);
+ Soldier noSpace;noSpace.inv.accept=false;ERR_EnemyMineGrant noLoose;noLoose.Check(&mode,&noSpace.group,&noSpace.actor);assert(noLoose.m_Finished&&noSpace.inv.requests==1&&!noLoose.ReadyForResupply(true));
+ noSpace.inv.Complete(false);assert(noLoose.ReadyForResupply(true)); // Finished refusal settles uncertainty; no extra startup request.
Soldier timeoutActor;ERR_EnemyMineGrant timeoutGrant;
timeoutGrant.Check(&mode,&timeoutActor.group,&timeoutActor.actor);
for(int i=0;i<12;i++)timeoutGrant.Check(&mode,&timeoutActor.group,&timeoutActor.actor);
- assert(timeoutGrant.m_Finished&&!timeoutGrant.ReadyForResupply()&&timeoutActor.inv.requests==1);
- timeoutActor.inv.Complete(true);assert(timeoutGrant.ReadyForResupply());
+ assert(timeoutGrant.m_Finished&&!timeoutGrant.ReadyForResupply()&&!timeoutGrant.ReadyForResupply(true)&&timeoutActor.inv.requests==1);
+ timeoutActor.inv.Complete(true);assert(timeoutGrant.ReadyForResupply()&&timeoutGrant.ReadyForResupply(true));
+ mode.settings.mines=0;assert(timeoutGrant.ReadyForResupply(true)); // Current setting cannot confer or revoke original designation.
timeoutGrant.Check(&mode,&timeoutActor.group,&timeoutActor.actor);assert(timeoutActor.inv.requests==1);
}
--- a/tests/test_enemy_equipment.py
+++ b/tests/test_enemy_equipment.py
@@ -36,7 +36,7 @@
void Check(ERR_GameMode*){checks++;}void Stop(){stops++;}
};
struct ERR_EnemyRadio{ERR_EnemyRadio(SCR_AIGroup*,int){}void Register(IEntity*){}void Check(ERR_GameMode*){}void Stop(){}};
-struct MineGrant{bool settled=false;bool ReadyForResupply(){return settled;}};
+struct MineGrant{bool settled=false,designated=false;bool ReadyForResupply(bool replacement=false){return settled&&(!replacement||designated);}};
struct ERR_EnemyEquipmentEntry{
MineGrant* Mines=nullptr;
ERR_EnemyOptics* Optics=nullptr;bool Complete=false;
@@ -53,9 +53,9 @@
assert(entry->Complete&&entry->Optics->checks==1&&combat.refreshes==1); // Stinger is already complete; optics has its own startup completion.
MineGrant grant;entry->Mines=&grant;IEntity foreign;SCR_AIGroup otherGroup;
assert(!service.MineSupplyReady(&group,&soldier));grant.settled=true;
- assert(service.MineSupplyReady(&group,&soldier));assert(!service.MineSupplyReady(&otherGroup,&soldier));assert(!service.MineSupplyReady(&group,&foreign));
+ assert(service.MineSupplyReady(&group,&soldier));assert(!service.MineSupplyReady(&group,&soldier,true));grant.designated=true;assert(service.MineSupplyReady(&group,&soldier,true));assert(!service.MineSupplyReady(&otherGroup,&soldier,true));assert(!service.MineSupplyReady(&otherGroup,&soldier));assert(!service.MineSupplyReady(&group,&foreign));
soldier.eligible=false;assert(!service.MineSupplyReady(&group,&soldier));soldier.eligible=true;
- entry->Released=true;assert(!service.MineSupplyReady(&group,&soldier));entry->Released=false;
+ entry->Released=true;assert(!service.MineSupplyReady(&group,&soldier));assert(!service.MineSupplyReady(&group,&soldier,true));entry->Released=false;
entry->Mines=nullptr;assert(!service.MineSupplyReady(&group,&soldier));entry->Mines=&grant;
service.m_Stopped=true;assert(!service.MineSupplyReady(&group,&soldier));service.m_Stopped=false;
service.m_Entries.resize(2049,nullptr);assert(!service.MineSupplyReady(&group,&soldier));service.m_Entries.resize(1);
--- a/tests/test_infantry_mine_transfer.py
+++ b/tests/test_infantry_mine_transfer.py
@@ -12,12 +12,13 @@
stored=body(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c',' static bool StoredUnder(')
def cpp(s):
s=re.sub(r'//[^\n]*','',s).replace('protected ','').replace('override ','').replace('ref ','').replace('class ','struct ').replace('foreach (','for (').replace('array<','Array<').replace('null','nullptr')
+ s=s.replace('out IEntity found','IEntity& found').replace('ERR_HQ.Cast(m_Mode.Command())','m_Mode.Command()')
s=s.replace('void ERR_InfantryMineTransfer(', 'ERR_InfantryMineTransfer(').replace('void ERR_FiniteMagazineReservation(', 'ERR_FiniteMagazineReservation(')
for typ in ('InventoryStorageManagerComponent','InventoryItemComponent','SCR_AIVehicleUsageComponent','SCR_PressureTriggerComponent'):
s=re.sub(typ+r'\.Cast\((\w+)\.FindComponent\('+typ+r'\)\)',r'\1.'+{'InventoryStorageManagerComponent':'inventory','InventoryItemComponent':'item','SCR_AIVehicleUsageComponent':'usage','SCR_PressureTriggerComponent':'trigger'}[typ],s)
for typ,field in [('InventoryStorageManagerComponent','inventory'),('SCR_AntiPersonnelMineCollisionHandlerComponent','collision'),('SCR_PressureTriggerComponent','trigger'),('DamageManagerComponent','damage')]:
s=re.sub(r'(\w+)\.FindComponent\('+typ+r'\)',r'\1.'+field,s)
- for typ in ('ERR_GameMode','ERR_ForceRecord','ERR_HQStatusSampler','InventoryStorageManagerComponent','InventoryItemComponent','InventoryStorageSlot','BaseInventoryStorageComponent','SCR_AIVehicleUsageComponent','SCR_PressureTriggerComponent','SCR_AIGroupUtilityComponent','Physics','ERR_InfantryMineTransfer','ERR_InfantryMineCallback','ERR_FiniteMagazineReservation'):
+ for typ in ('ERR_HQ','ERR_GameMode','ERR_ForceRecord','ERR_HQStatusSampler','InventoryStorageManagerComponent','InventoryItemComponent','InventoryStorageSlot','BaseInventoryStorageComponent','SCR_AIVehicleUsageComponent','SCR_PressureTriggerComponent','SCR_AIGroupUtilityComponent','Physics','ERR_InfantryMineTransfer','ERR_InfantryMineCallback','ERR_FiniteMagazineReservation'):
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
s=s.replace('Array<BaseInventoryStorageComponent>','Array<BaseInventoryStorageComponent*>').replace('Array<IEntity> attempted','Array<IEntity>* attempted')
for field in ('m_Original','m_Spent'):
@@ -25,7 +26,7 @@
s=s.replace('Array<IEntity> original, Array<IEntity> spent','Array<IEntity>* original, Array<IEntity>* spent')
for ns in ('Replication','SCR_Global','ERR_GameMode','ERR_Actors','ERR_EnemyEquipmentRules','ERR_ConvoyRules','SCR_FactionManager','CompartmentAccessComponent','ERR_InfantryAmmoTransfer','ERR_InfantryAmmoAccess','ERR_ForceRules','ERR_EnemyLoadoutCatalog','ERR_InfantryAmmoTransferState','EStoragePurpose'):
s=s.replace(ns+'.',ns+'::')
- for var in ('m_Mine','exactMine','mode','donorVehicle','recipientActor','originalRoster','usage','item','actor','group','inventory','carried','trigger','m_Mode','m_Donor','m_DonorGroup','m_Recipient','m_RecipientGroup','m_Roster','m_DonorRecord','m_RecipientRecord','m_Source','m_Destination','m_Reservation','m_Callback','m_FromSlot','m_FromStorage','m_Storage','slot','storage','utility','physics','Transfer','entity'):
+ for var in ('hq','m_Mine','exactMine','mode','donorVehicle','recipientActor','originalRoster','usage','item','actor','group','inventory','carried','trigger','m_Mode','m_Donor','m_DonorGroup','m_Recipient','m_RecipientGroup','m_Roster','m_DonorRecord','m_RecipientRecord','m_Source','m_Destination','m_Reservation','m_Callback','m_FromSlot','m_FromStorage','m_Storage','slot','storage','utility','physics','Transfer','entity'):
s=re.sub(r'\b'+var+r'\.',var+'->',s)
for call in ('Forces()','GetPilotCompartmentSlot()','GetPrefabData()','GetParentSlot()'):
s=s.replace(call+'.',call+'->')
@@ -60,7 +61,8 @@
struct Group{bool slave=false;int players=0,faction=1;SCR_AIGroupUtilityComponent*utility=nullptr;bool IsSlave(){return slave;}int GetPlayerCount(bool){return players;}int GetFaction(){return faction;}SCR_AIGroupUtilityComponent*GetGroupUtilityComponent(){return utility;}};
struct ERR_ForceRecord{SCR_AIGroup Group=nullptr;bool Physical=true,Eliminated=false;};
struct Ledger{SCR_AIGroup donor=nullptr,recipient=nullptr;ERR_ForceRecord*d=nullptr,*r=nullptr;ERR_ForceRecord*FindGroup(SCR_AIGroup g){return g==donor?d:g==recipient?r:nullptr;}};
-struct ERR_GameMode{inline static ERR_GameMode*current=nullptr;bool active=true;Ledger*ledger=nullptr;static ERR_GameMode*Get(){return current;}bool Active(){return active;}Ledger*Forces(){return ledger;}int EnemyFaction(){return 1;}};
+struct ERR_HQ{bool designated=true;bool MineSupplyReady(SCR_AIGroup,IEntity,bool replacement=false){return !replacement||designated;}};
+struct ERR_GameMode{ERR_HQ hq;bool command=true;ERR_HQ*Command(){return command?&hq:nullptr;}inline static ERR_GameMode*current=nullptr;bool active=true;Ledger*ledger=nullptr;static ERR_GameMode*Get(){return current;}bool Active(){return active;}Ledger*Forces(){return ledger;}int EnemyFaction(){return 1;}};
struct ERR_HQStatusSampler{SCR_AIGroup group=nullptr;IEntity actor=nullptr;bool valid=true;int strength=1;bool OwnsRoster(SCR_AIGroup g){return g==group;}int InitialStrength(){return strength;}IEntity InitialMember(int i){return i==0?actor:nullptr;}bool CanAttribute(int i,IEntity a,bool){return valid&&i==0&&a==actor;}};
struct Replication{inline static bool server=true;static bool IsServer(){return server;}};
struct SCR_Global{inline static bool edit=false;static bool IsEditMode(){return edit;}};
@@ -95,10 +97,19 @@
from.storage=&sourceStorage;from.attached=&incoming;to.storage=&destStorage;ownSlot.storage=&destStorage;ownSlot.attached=&original;extraSlot.storage=&destStorage;extraSlot.attached=&extra;
incoming.prefab=&prefab;incoming.trigger=&inTrigger;incoming.item=&inItem;inItem.slot=&from;original.prefab=&prefab;original.trigger=&ownTrigger;original.item=&ownItem;ownItem.slot=&ownSlot;extra.prefab=&prefab;extra.trigger=&extraTrigger;extra.item=&extraItem;extraItem.slot=&extraSlot;
source.items={&incoming};destination.items={&original};roster.group=&recipientGroup;roster.actor=&actor;stock={&incoming};assert(reservation.Reserve());}
- ERR_InfantryMineTransfer Job(){return ERR_InfantryMineTransfer(&mode,&truck,&donorGroup,&actor,&recipientGroup,&incoming,&reservation,&roster,0);}
+ ERR_InfantryMineTransfer Job(bool empty=false){return ERR_InfantryMineTransfer(&mode,&truck,&donorGroup,&actor,&recipientGroup,&incoming,&reservation,&roster,0,empty);}
void Move(){source.items.clear();destination.items.Insert(&incoming);from.attached=nullptr;to.attached=&incoming;inItem.slot=&to;}
};
int main(){
+ // Authoritative designation plus an inspected empty inventory permits exactly one finite replacement.
+ {Fixture f;f.destination.items.clear();auto t=f.Job(true);assert(t.Tick(0)==PENDING&&t.Submitted());f.Move();f.source.last->OnComplete();assert(t.Tick(1)==SUCCEEDED&&f.destination.items.Count()==1&&f.source.calls==1);}
+ for(int k=0;k<7;k++){Fixture f;f.destination.items.clear();
+ if(k==0)f.mode.hq.designated=false;if(k==1)f.destination.items.Insert(nullptr);if(k==2)f.destination.items.Insert(&f.original);if(k==3)f.destination.owner=&f.extra;if(k==4)f.destination.items.resize(129,&f.original);if(k==5){f.original.damage=false;f.destination.items.Insert(&f.original);}if(k==6)f.mode.command=false;
+ auto t=f.Job(true);assert(t.Tick(0)==FAILED&&!t.Submitted()&&f.source.calls==0);}
+ // Empty/one mode never switches after dispatch; a foreign new mine or revoked entitlement aborts and retains uncertainty.
+ for(int k=0;k<2;k++){Fixture f;f.destination.items.clear();auto t=f.Job(true);t.Tick(0);if(k==0)f.destination.items.Insert(&f.original);if(k==1){f.mode.hq.designated=false;}assert(t.Tick(1)==FAILED&&t.Unresolved()&&f.source.calls==1);f.Move();f.source.last->OnComplete();assert(!t.Unresolved()&&t.Tick(2)==FAILED);}
+ {Fixture f;f.destination.items.clear();auto t=f.Job(true);t.Tick(0);t.Stop();f.Move();f.source.last->OnComplete();assert(!t.Unresolved()&&t.Tick(1)==FAILED&&f.source.calls==1&&!f.reservation.ReleaseUnsubmitted());}
+
{Fixture f;auto t=f.Job();assert(t.Tick(0)==PENDING&&t.Submitted()&&t.Unresolved()&&f.source.calls==1);assert(f.reservation.CommittedFor(&f.incoming)&&!f.reservation.ReleaseUnsubmitted());
f.source.last->OnComplete();assert(t.Tick(1)==PENDING&&t.Unresolved());f.Move();assert(t.Tick(2)==SUCCEEDED&&!t.Unresolved());assert(f.destination.items.Count()==2);t.Tick(3);assert(f.source.calls==1);}
// Partially constructed contexts never bind later or reach native mutation.
@@ -134,4 +145,4 @@
{Fixture f;auto t=f.Job();t.Tick(0);f.roster.valid=false;assert(t.Tick(1)==FAILED&&t.Unresolved());f.Move();f.source.last->OnComplete();assert(!t.Unresolved()&&t.Tick(2)==FAILED);}
}
'''.replace(';if(', ';\n if('))
-print('PASS: actual finite PMN4 one-to-two entitlement, pinned original ownership, shared commit, physical custody, callback quarantine/deletion and no retry')
+print('PASS: actual finite PMN4 designated-zero/one entitlement, pinned original ownership, shared commit, physical custody, callback quarantine/deletion and no retry')
--- a/tests/test_infantry_mine_coordinator.py
+++ b/tests/test_infantry_mine_coordinator.py
@@ -18,23 +18,24 @@
#include <cmath>
using string=std::string;
struct Vec{float x=0;static float DistanceXZ(Vec a,Vec b){return std::abs(a.x-b.x);}};
-struct Group{};struct Mode{};struct Entity{bool ready=false,fight=true,owned=true,grant=true;int mines=1;Vec pos;Vec GetOrigin(){return pos;}};using IEntity=Entity*;
+struct Group{};struct Mode{};struct Entity{bool ready=false,fight=true,owned=true,grant=true,designated=false;int mines=1;Vec pos;Vec GetOrigin(){return pos;}};using IEntity=Entity*;
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}void Insert(T v){this->push_back(v);}bool Contains(T v){return std::find(this->begin(),this->end(),v)!=this->end();}void RemoveItem(T v){this->erase(std::remove(this->begin(),this->end(),v),this->end());}};
'''+translated_reservation()+r'''
struct Sampler{Array<IEntity>original;int InitialStrength(){return original.Count();}IEntity InitialMember(int i){return original[i];}bool CanAttribute(int i,IEntity x,bool){return i>=0&&i<InitialStrength()&&original[i]==x&&x->owned;}};
struct Unit{struct Group*Group;Sampler*StatusSampler;};struct Truck{IEntity Entity;struct Group*Group;};
-struct HQ{bool MineSupplyReady(Group*,IEntity x){return x&&x->grant;}};
+struct HQ{bool MineSupplyReady(Group*,IEntity x,bool replacement=false){return x&&x->grant&&(!replacement||x->designated);}};
struct ERR_Actors{static bool CanFight(IEntity x){return x&&x->fight;}};
struct ERR_InfantryMineTransfer{
static inline bool accept=true;static inline int made=0;IEntity item,actor;ERR_FiniteMagazineReservation*r;bool submitted=false;int result=0;
- ERR_InfantryMineTransfer(Mode*,IEntity,Group*,IEntity a,Group*,IEntity i,ERR_FiniteMagazineReservation*q,Sampler*,int):item(i),actor(a),r(q){made++;}
+ ERR_InfantryMineTransfer(Mode*,IEntity,Group*,IEntity a,Group*,IEntity i,ERR_FiniteMagazineReservation*q,Sampler*,int,bool empty=false):item(i),actor(a),r(q){assert(a->mines==(empty?0:1));made++;}
static bool ReadyMine(IEntity x){return x&&x->ready;}
+ static int CarriedCount(IEntity x,Group*,IEntity& found){found=x;return x?x->mines:-1;}
static IEntity Carrier(IEntity x,Group*){return x&&x->mines==1?x:nullptr;}
int Tick(float){if(accept&&!submitted){assert(r->Commit());submitted=true;}return result;}
bool Submitted(){return submitted;}
};
struct Service{
- HQ*m_HQ;Mode*m_Mode;Unit*m_Recipient;Truck*m_Truck;IEntity m_Receiving=nullptr;int m_MineRecipientSlot=-1,m_MinesDelivered=0,finishes=0;ERR_InfantryMineTransfer*m_MineMove=nullptr;
+ HQ*m_HQ;Mode*m_Mode;Unit*m_Recipient;Truck*m_Truck;IEntity m_Receiving=nullptr;bool m_EmptyMineReplacement=false;int m_MineRecipientSlot=-1,m_MinesDelivered=0,finishes=0;ERR_InfantryMineTransfer*m_MineMove=nullptr;
Array<IEntity>m_Members,m_MineStock,m_Spent,cargo;
bool Contains(IEntity,IEntity x){return cargo.Contains(x);}void Finish(string){finishes++;}
'''+s+r'''
@@ -43,7 +44,11 @@
Fixture():sampler{{&actor,&other}},unit{&g,&sampler},donor{&truck,&d}{mine.ready=second.ready=true;s.m_HQ=&h;s.m_Mode=&mode;s.m_Recipient=&unit;s.m_Truck=&donor;s.m_Members={&actor,&other};s.m_MineStock={&mine,&second};s.cargo=s.m_MineStock;ERR_InfantryMineTransfer::accept=true;}
};
int main(){
- // Never grant mines to zero/two-mine members or to unsettled initial grants.
+ {Fixture f;f.actor.mines=0;f.actor.designated=true;f.s.TransferMine(0);assert(f.s.m_MineMove&&f.s.m_MineMove->actor==&f.actor&&f.s.m_EmptyMineReplacement&&f.s.m_Spent.Count()==1);}
+ {Fixture f;f.actor.mines=-1;f.actor.designated=true;f.other.mines=0;f.s.TransferMine(0);assert(!f.s.m_MineMove&&f.s.m_Spent.empty());}
+ {Fixture f;f.actor.mines=0;f.actor.designated=true;ERR_InfantryMineTransfer::accept=false;f.s.TransferMine(0);assert(f.s.m_Receiving==&f.actor&&f.s.m_EmptyMineReplacement);f.actor.mines=1;ERR_InfantryMineTransfer::accept=true;f.s.TransferMine(1);assert(!f.s.m_MineMove&&f.s.finishes==1&&f.s.m_Spent.empty());}
+
+ // Never grant mines to undesignated zero/two-mine members or to unsettled initial grants.
for(int refusal=0;refusal<6;refusal++){Fixture f;f.other.mines=0;
if(refusal==0)f.actor.mines=0;if(refusal==1)f.actor.mines=2;if(refusal==2)f.actor.grant=false;if(refusal==3)f.actor.owned=false;if(refusal==4)f.actor.pos.x=5;if(refusal==5)f.actor.fight=false;
f.s.TransferMine(0);assert(!f.s.m_MineMove&&f.s.m_Spent.empty()&&!f.s.m_Receiving);}
@@ -62,16 +67,16 @@
{Fixture f;f.s.TransferMine(0);auto*leaf=f.s.m_MineMove;leaf->result=-1;f.s.TransferMine(1000);assert(f.s.m_MineMove==leaf&&f.s.finishes==1&&f.s.m_Spent.Count()==1);}
}
'''.replace('if(refusal==0)f.actor.mines=0;if(refusal==1)f.actor.mines=2;if(refusal==2)f.actor.grant=false;if(refusal==3)f.actor.owned=false;if(refusal==4)f.actor.pos.x=5;if(refusal==5)f.actor.fight=false;', 'if(refusal==0){f.actor.mines=0;}if(refusal==1){f.actor.mines=2;}if(refusal==2){f.actor.grant=false;}if(refusal==3){f.actor.owned=false;}if(refusal==4){f.actor.pos.x=5;}if(refusal==5){f.actor.fight=false;}'))
-print('PASS: actual one-mine original carrier selection, settled startup, shared finite claims, no refund after submission')
+print('PASS: actual designated zero/one original carrier selection, settled startup, shared finite claims, no refund after submission')
h=Path(os.environ.get('ERR_HQ_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'))
w=body(h,'bool MineSupplyReady(').replace('SCR_AIGroup group','Group* group').replace('Replication.','Replication::').replace('m_Equipment.','m_Equipment->')
run(r'''
#include <cassert>
struct Group{};struct Item{};using IEntity=Item*;
-struct Equipment{bool ready=false;Group*group=nullptr;IEntity actor=nullptr;bool MineSupplyReady(Group*g,IEntity a){return ready&&g==group&&a==actor;}};
+struct Equipment{bool ready=false,designated=false;Group*group=nullptr;IEntity actor=nullptr;bool MineSupplyReady(Group*g,IEntity a,bool replacement=false){return ready&&g==group&&a==actor&&(!replacement||designated);}};
struct Replication{static inline bool server=true;static bool IsServer(){return server;}};
struct HQ{Equipment*m_Equipment=nullptr;bool active=true;bool MissionActive(){return active;}
'''+w+r'''};
-int main(){Group group,other;Item actor;Equipment e;e.group=&group;e.actor=&actor;HQ h;assert(!h.MineSupplyReady(&group,&actor));h.m_Equipment=&e;assert(!h.MineSupplyReady(&group,&actor));e.ready=true;assert(h.MineSupplyReady(&group,&actor));assert(!h.MineSupplyReady(&other,&actor));h.active=false;assert(!h.MineSupplyReady(&group,&actor));h.active=true;Replication::server=false;assert(!h.MineSupplyReady(&group,&actor));}
+int main(){Group group,other;Item actor;Equipment e;e.group=&group;e.actor=&actor;HQ h;assert(!h.MineSupplyReady(&group,&actor));h.m_Equipment=&e;assert(!h.MineSupplyReady(&group,&actor));e.ready=true;assert(h.MineSupplyReady(&group,&actor));assert(!h.MineSupplyReady(&group,&actor,true));e.designated=true;assert(h.MineSupplyReady(&group,&actor,true));assert(!h.MineSupplyReady(&other,&actor,true));assert(!h.MineSupplyReady(&other,&actor));h.active=false;assert(!h.MineSupplyReady(&group,&actor));h.active=true;Replication::server=false;assert(!h.MineSupplyReady(&group,&actor));}
''')
print('PASS: actual HQ startup grant readiness forwarding is authoritative and fail-closed')
--- a/tests/test_infantry_resupply_planning.py
+++ b/tests/test_infantry_resupply_planning.py
@@ -23,6 +23,7 @@
s=s.replace(name+'.',name+'->')
for name in ('Forces()','GetPrefabData()','Convoys()'):s=s.replace(name+'.',name+'->')
s=s.replace('ERR_SupplyConvoyTask* supply', 'Convoy* supply')
+ s=s.replace('bool designated;','bool designated=false;')
s=s.replace('bool available;','bool available=false;').replace('bool living;','bool living=false;')
return s
rules='\n'.join(cpp(body(P,x)) for x in ('static bool Fresh(', 'static bool Need(', 'static bool Pair(', 'static bool MinePair('))
@@ -85,10 +86,11 @@
struct ERR_InfantryMineTransfer{static bool ReadyMine(IEntity x){return x&&x->readyMine;}};
struct Route{};struct Leaf{bool unresolved=false;void Stop(){}bool Unresolved(){return unresolved;}};struct Mounted{bool busy=false;bool Busy(){return busy;}void Stop(){}};
struct HQ{Mode* m_Mode;Array<ERR_HQUnit>m_Units;Convoy* convoy=nullptr;ConvoyService convoys;ConvoyService* Convoys(){return &convoys;}Route route;bool active=true,routeAvailable=true;int downlinks=0;bool MissionActive(){return active;}Convoy* ConvoyTask(){return convoy;}
+ bool designated=false;bool MineSupplyReady(Group*,IEntity,bool replacement=false){return !replacement||designated;}
Route* Downlink(Group*,string,string,int){downlinks++;return routeAvailable?&route:nullptr;}
'''+reserve+r'''
};
-struct Service{HQ* m_HQ;Mode* m_Mode;Convoy* m_Convoy=nullptr;Truck* m_Truck=nullptr;Unit* m_Recipient=nullptr;Unit* m_Donor=nullptr;Leaf* m_Move=nullptr;Leaf*m_MineMove=nullptr;MineBatch*m_MineBatch=nullptr;bool m_MineTrip=false,m_MineStockCaptured=false;int m_MineRecipientSlot=-1;Mounted*m_Mounted=nullptr;Route* m_Order=nullptr;IEntity m_Receiving=nullptr;
+struct Service{HQ* m_HQ;Mode* m_Mode;Convoy* m_Convoy=nullptr;Truck* m_Truck=nullptr;Unit* m_Recipient=nullptr;Unit* m_Donor=nullptr;Leaf* m_Move=nullptr;Leaf*m_MineMove=nullptr;MineBatch*m_MineBatch=nullptr;bool m_MineTrip=false,m_MineStockCaptured=false,m_EmptyMineReplacement=false;int m_MineRecipientSlot=-1;Mounted*m_Mounted=nullptr;Route* m_Order=nullptr;IEntity m_Receiving=nullptr;
Array<IEntity>m_Stock,m_MineStock,m_Spent,m_Members,m_Served;Map<int>*m_Consumed=new Map<int>();Map<float>*m_NextAttempt=new Map<float>();int m_ConvoySlot=-1;float m_NextTick=0;int m_State=0,m_Trip=0,m_Token=0,m_ReportId=0,m_TripDelivered=0;float m_Deadline=0,m_StockDeadline=-1;bool m_Stopped=false;
Waypoint* m_MoveWaypoint=nullptr;Waypoint* m_HoldWaypoint=nullptr;string reason;bool Current(){return !m_Stopped&&Replication::IsServer()&&m_HQ->active;}void Audit(string why){reason=why;}
'''+methods+r'''
@@ -179,9 +181,10 @@
{Fixture f;for(int i=48;i<96;i++)f.inventory.items.Insert(&f.items[i]);f.items[0].prefab.name="NATO_556_STANAG";
f.items[96].magazine=false;f.inventory.items.Insert(&f.items[96]);f.s.Stock(0);
assert(!f.s.m_Stopped&&f.s.m_State==1&&f.s.m_Stock.Count()==96&&f.s.m_Stock.Contains(&f.items[0])&&!f.s.m_Stock.Contains(&f.items[96]));}
- // Mine demand is a detached known-one report, never a grant to zero or unknown.
+ // Detached known-zero demand also requires settled original designation; unknown never authorizes.
{Fixture f;MineBatch batch;f.convoy.supply=true;f.convoy.MineStock=&batch;f.items[48].readyMine=true;batch.items={&f.items[48]};f.inventory.items.Insert(&f.items[48]);f.s.Stock(0);
for(int n: {0,2,3}){f.ammo.ReadyMines=n;assert(!f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));}
+ f.ammo.ReadyMines=0;f.hq.designated=true;f.one.released=f.two.released=true;assert(!f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));f.one.released=f.two.released=false;assert(f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));f.s.Finish("reset");f.s.m_Consumed->data.clear();f.s.m_NextAttempt->data.clear();f.hq.designated=false;
f.ammo.ReadyMines=1;f.ammo.MineComplete=false;assert(!f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));f.ammo.MineComplete=true;
batch.complete=false;assert(!f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));batch.complete=true;
assert(f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));assert(f.s.m_MineTrip&&f.s.m_MineStock.Count()==1);}
--- a/tests/test_enemy_radio.py
+++ b/tests/test_enemy_radio.py
@@ -39,7 +39,7 @@
float worldTime=0;
struct SCR_AICombatComponent{void ERR_BindEnemySkill(ERR_GameMode*,Group*){} void ERR_RefreshEnemySkill(){}};
struct ERR_EnemyOptics{ERR_EnemyOptics(IEntity,Group*){}void Check(ERR_GameMode*){}};
-struct MineGrant{bool ReadyForResupply(){return false;}};
+struct MineGrant{bool ReadyForResupply(bool replacement=false){return false;}};
struct ERR_EnemyEquipmentEntry{MineGrant*Mines=nullptr;IEntity Actor=nullptr;struct Group*Group=nullptr;SCR_AICombatComponent*Combat=nullptr;ERR_EnemyOptics*Optics=nullptr;bool Released=false;int SkillChecks=0;
void Check(ERR_GameMode*){}void Stop(ERR_GameMode*){Released=true;}};
'''
@@ -0,0 +1,7 @@
{
"reviewer": "hq_memory_review",
"approved": true,
"focused_programs_passed": 6,
"findings": "Known-zero admission requires original settled designated member before reservation; unknown inventory fails closed; fixed actor/mode, finite identities, settled callback and unresolved cross-truck claims retained. No stock generation, refunds or placement reset.",
"limits": "Review and actual-method tests; physical native transfer not qualified."
}
@@ -0,0 +1,64 @@
Test audio routed to silent output and muted (stream 677451)
{
"stopped_unix": 1790898323.6916618,
"console_sha256": "440bbc19a6b6ddd417e30494532572a26a5a00c4194bb6d52560aef739a0382e",
"end_source_sha256": "774377afcda0eee0e9fa2075129fdcf080a7b431c84b4885860d2d82b8c08e54",
"source_unchanged_after_run": true,
"source_provenance": "launch_snapshot",
"launch_id": "73ac15a8-7f48-4dc7-8b8b-28adae990e08",
"launch_source_manifest": "launch-source-sha256.json",
"launch_source_metadata": "launch-source.json",
"launch_source_sha256": "774377afcda0eee0e9fa2075129fdcf080a7b431c84b4885860d2d82b8c08e54",
"external_addons_unchanged_after_run": true,
"external_addon_count": 7,
"external_addon_inputs": "external-addons-inputs.json",
"started_unix": 1790898164.1684012,
"result": "smoke-completed",
"result_before_cleanup": "smoke-completed",
"cleanup_confirmed": true,
"cleanup_report": {
"cleanup_confirmed": true,
"idle_verified": true,
"errors": [],
"orphan_cleanup": {
"attempted": true,
"report": {
"scope": "/tmp/erri-headless-compat/pfx",
"candidates": [],
"term_sent": [],
"kill_sent": [],
"cleared": true
}
}
},
"runner_error": null,
"session": "/home/jakub/devel/escapistsrereintroduced/.build/mine-zero-profile-2252/logs/logs_2026-10-02_01-42-45",
"rounds_required": 1,
"project": "chat",
"display": "headless Gamescope 1280x720/30Hz",
"dedicated_server": false,
"audio": "silent sink and per-test-stream mute"
}
{
"log": "/home/jakub/devel/escapistsrereintroduced/.build/mine-zero-profile-2252/logs/logs_2026-10-02_01-42-45/console.log",
"smoke_completions": 1,
"assertions_passed": 63,
"script_or_runtime_failures": [],
"engine_errors": [
"01:42:47.846 RESOURCES (E): Wrong GUID/name for resource @\"{0AFDD005CD669892}Language/rhs_localization.st\" in property \"StringTableSource\"",
"01:42:47.847 RESOURCES (E): Wrong GUID/name for resource @\"{41FA2A08B6971D4C}Language/wcs_armament_localization.st\" in property \"StringTableSource\"",
"01:42:47.847 RESOURCES (E): Wrong GUID/name for resource @\"{84270E09F7A4A7A0}Language/wcs_spacecore_localization.st\" in property \"StringTableSource\"",
"01:42:47.847 RESOURCES (E): Wrong GUID/name for resource @\"{E50E557B468C7073}Language/wcs_bmp1_localization.st\" in property \"StringTableSource\"",
"01:43:27.106 WORLD (E): Unknown keyword/data 'Parent' at offset 35955(0x8c73)",
"01:43:27.106 WORLD (E): Unknown keyword/data 'Parent' at offset 36915(0x9033)",
"01:43:27.108 WORLD (E): Unknown keyword/data 'Parent' at offset 42646(0xa696)",
"01:43:27.108 WORLD (E): Unknown keyword/data 'Parent' at offset 43645(0xaa7d)",
"01:43:27.108 WORLD (E): Unknown keyword/data 'Parent' at offset 44617(0xae49)",
"01:43:27.108 WORLD (E): Unknown keyword/data 'Parent' at offset 45590(0xb216)",
"01:43:27.217 WORLD (E): Unknown keyword/data 'SlidingTrackMaterial' at offset 19441(0x4bf1)",
"01:43:33.542 WORLD (E): Unknown keyword/data 'm_HardLandingVelocity' at offset 133(0x85)",
"01:43:33.542 WORLD (E): Unknown keyword/data 'm_DeathLandingVelocity' at offset 163(0xa3)"
],
"missing_end_screen": [],
"scope": "scripted local Workbench integration only; no multiplayer, piloting or FPS claim"
}
@@ -0,0 +1,250 @@
[
{
"original_path": "/tmp/erri-1208-baseline",
"retained_path": "/home/jakub/devel/escapistsrereintroduced/.build/retained-temporary-snapshots-2252/erri-1208-baseline",
"entries": {
"hq-before.json": {
"type": "file",
"bytes": 565,
"sha256": "96d11aa4e570b936f287777a2b1b927d16398f280e53172963c20eec5edf0b8e"
},
"hq-integrated.json": {
"type": "file",
"bytes": 565,
"sha256": "fb8f83fb2cd6b874411a2e611c9dcfedcfda1a69f5a301b435e26906f349a743"
},
"staged.patch": {
"type": "file",
"bytes": 27569586,
"sha256": "d914207bef27b52701e88e2b425d43a2d0706a9c95c1d1ab1dbbb3c12dc073c0"
},
"status.txt": {
"type": "file",
"bytes": 74161,
"sha256": "71a374199d2ab5a4cbe05b885e7da419543b4ea89378bedf65760fee4e9e3ce6"
},
"unstaged.json": {
"type": "file",
"bytes": 6265,
"sha256": "bf49e571e065511f503c83a13df0ea145e5c7971b455b855134a1bf43b31f43f"
},
"unstaged.patch": {
"type": "file",
"bytes": 164476,
"sha256": "f11abcce2ed21ea5521fe8527c1b587c538ccc805abff95a0b88222972bad038"
}
},
"verified_before_removal": true,
"replaced_by_symlink": true
},
{
"original_path": "/tmp/erri-dependencies-review-baseline",
"retained_path": "/home/jakub/devel/escapistsrereintroduced/.build/retained-temporary-snapshots-2252/erri-dependencies-review-baseline",
"entries": {
"previous-handoff.json": {
"type": "file",
"bytes": 53059,
"sha256": "5e6da8d3a687d66d30befd2a13e19a4d0fc8443818e1a35f65459ead768e810b"
},
"staged.patch": {
"type": "file",
"bytes": 29305601,
"sha256": "d0f71f80e088e25f316dfc366b1bd55533aaabd593f3b85b825892b0f6113835"
},
"status.txt": {
"type": "file",
"bytes": 64478,
"sha256": "4af5e3c3a625309934f193edd53c0194f04017ce6f6da62e729c5d639983dcfd"
},
"unstaged.json": {
"type": "file",
"bytes": 6265,
"sha256": "5af66d27404ee9f800edcd98faafd7c1759f9d9dfe68544f6049758a2c6e115b"
},
"unstaged.patch": {
"type": "file",
"bytes": 164036,
"sha256": "e6c931758081747ca1a76982c9419afdfde26feac6681f36c40a2a9a627f7b42"
}
},
"verified_before_removal": true,
"replaced_by_symlink": true
},
{
"original_path": "/tmp/erri-mine-intel-1033-baseline",
"retained_path": "/home/jakub/devel/escapistsrereintroduced/.build/retained-temporary-snapshots-2252/erri-mine-intel-1033-baseline",
"entries": {
"authored.json": {
"type": "file",
"bytes": 50013,
"sha256": "d97e0f733e1b0e581d28923247c96e9c001604b208f67e012c0ac59a7f0ed99b"
},
"handoff.json": {
"type": "file",
"bytes": 53584,
"sha256": "9844f97eabc1acd60eee2e8de9f5bb6983b7439950e133a55d1495a41b2cdb4f"
},
"registration.json": {
"type": "file",
"bytes": 211,
"sha256": "3971c7c3a5ca8c0c8164c5a44e3e5ea1aef97fca0785917c27c53d11e12e6307"
},
"staged.patch": {
"type": "file",
"bytes": 31627338,
"sha256": "ee975f1c04443363bed4554c32f0045758eae5e92a459b3734f3840697dec839"
},
"status.txt": {
"type": "file",
"bytes": 70842,
"sha256": "da3f55f31429a2fc1306f068e8ed2b30485eaf35a9a0aa6e68780d0303a7ff06"
},
"unstaged.json": {
"type": "file",
"bytes": 6265,
"sha256": "5af66d27404ee9f800edcd98faafd7c1759f9d9dfe68544f6049758a2c6e115b"
},
"unstaged.patch": {
"type": "file",
"bytes": 164036,
"sha256": "e6c931758081747ca1a76982c9419afdfde26feac6681f36c40a2a9a627f7b42"
}
},
"verified_before_removal": true,
"replaced_by_symlink": true
},
{
"original_path": "/tmp/erri-refuel-1118-baseline",
"retained_path": "/home/jakub/devel/escapistsrereintroduced/.build/retained-temporary-snapshots-2252/erri-refuel-1118-baseline",
"entries": {
"addons": {
"type": "directory"
},
"addons/core": {
"type": "directory"
},
"addons/core/Scripts": {
"type": "directory"
},
"addons/core/Scripts/Game": {
"type": "directory"
},
"addons/core/Scripts/Game/ERR": {
"type": "directory"
},
"addons/core/Scripts/Game/ERR/40_Chat": {
"type": "directory"
},
"addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeSmoke.c": {
"type": "file",
"bytes": 53882,
"sha256": "120459ad24ba3f540080380544516a3be46bf3767b5e505d25c163a0a6791131"
},
"authored.json": {
"type": "file",
"bytes": 50140,
"sha256": "689fa448fc6eb830d24f69f22404a13b71474af6ccbb3b2a691a338d36863589"
},
"handoff.json": {
"type": "file",
"bytes": 53584,
"sha256": "9844f97eabc1acd60eee2e8de9f5bb6983b7439950e133a55d1495a41b2cdb4f"
},
"index": {
"type": "directory"
},
"index/addons": {
"type": "directory"
},
"index/addons/core": {
"type": "directory"
},
"index/addons/core/Scripts": {
"type": "directory"
},
"index/addons/core/Scripts/Game": {
"type": "directory"
},
"index/addons/core/Scripts/Game/ERR": {
"type": "directory"
},
"index/addons/core/Scripts/Game/ERR/40_Chat": {
"type": "directory"
},
"index/addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeSmoke.c": {
"type": "file",
"bytes": 53882,
"sha256": "120459ad24ba3f540080380544516a3be46bf3767b5e505d25c163a0a6791131"
},
"index/tests": {
"type": "directory"
},
"index/tests/test_native_memory_deadlines.py": {
"type": "file",
"bytes": 7352,
"sha256": "e3463a62c3b1b37d470760ac87fe6d692f1c4d7174c698fb3a40929ce13f4de2"
},
"index/tools": {
"type": "directory"
},
"index/tools/check.py": {
"type": "file",
"bytes": 12646,
"sha256": "eb0c1b3f0a4c64ccec0f1448c2ac8f08d9f062f3b91654c611d63ce6242d3f01"
},
"index/tools/headless_workbench.py": {
"type": "file",
"bytes": 22929,
"sha256": "d3eb561efbbfc1e795254aa4d821d034d4dec9ae4a6548e2072facc9afb1f1b4"
},
"own-runner-integration.json": {
"type": "file",
"bytes": 153,
"sha256": "d27de060909fdffac981a90f7413b184432cb89e737bd9acc4351a14f4ece27a"
},
"staged.patch": {
"type": "file",
"bytes": 32841098,
"sha256": "20aa4310eed2ac43f1b8313b9195b30980549ef8a457b379d1953977e448c86a"
},
"status.txt": {
"type": "file",
"bytes": 72764,
"sha256": "03e421b59697267a696833bc2244c19d5f268e8a12b84eecc4ac255527e4a42a"
},
"tests": {
"type": "directory"
},
"tests/test_native_memory_deadlines.py": {
"type": "file",
"bytes": 7352,
"sha256": "e3463a62c3b1b37d470760ac87fe6d692f1c4d7174c698fb3a40929ce13f4de2"
},
"tools": {
"type": "directory"
},
"tools/check.py": {
"type": "file",
"bytes": 12847,
"sha256": "25dcb5662d952bf549c1a554a4c3c5263c06478068aa491f1d5a73b104a88185"
},
"tools/headless_workbench.py": {
"type": "file",
"bytes": 23325,
"sha256": "37651b5601add5689143767ab1592f6753f2145177b1d8055babca1789eb6265"
},
"unstaged.json": {
"type": "file",
"bytes": 6265,
"sha256": "81f89bc9891b79757891faa2ccf3e5c78a62ee3981f3a2de0b1679ed76dc70c6"
},
"unstaged.patch": {
"type": "file",
"bytes": 164168,
"sha256": "f7cf12786f3b3bd41ef99d0f26ccd6ace959835517c7995fb077f72c724e196d"
}
},
"verified_before_removal": true,
"replaced_by_symlink": true
}
]
@@ -0,0 +1,713 @@
{
"initial_failure_log": "/home/jakub/devel/escapistsrereintroduced/.build/mine-zero-validation-2252/regressions.log",
"initial_failure_sha256": "d8125cb5aae0fd91a29292bf1b74010c7969cc4f5bafc75fa7ddf52ca7ce854d",
"check_sha256": "ba2aad12a973bbd5d1ce277b6175a55556a4347ac0286588d379163c75b372d1",
"total_registered": 272,
"registered_before_failed_test": 217,
"remaining": [
"test_native_package.py",
"test_item_station_teardown.py",
"test_source_bundle.py",
"test_native_flight_footprint.py",
"test_native_flight_input_diagnostics.py",
"test_mortar_logistics.py",
"test_mortar_courier_storage.py",
"test_powered_flight_pad.py",
"test_prison_escape_policy.py",
"test_powered_flight_policy.py",
"test_flight_action_runner.py",
"test_native_local_admission_probe.py",
"test_mortar_report_ledger.py",
"test_dedicated_native.py",
"test_native_mortar_transfer.py",
"test_mortar_aim_renewal.py",
"test_dedicated_preflight.py",
"test_mortar_transactions.py",
"test_native_mortar_probe.py",
"test_diagnostic_no_splash.py",
"test_casualty_incapacitation.py",
"test_casualty_remove_probe.py",
"test_extraction_placement_policy.py",
"test_guard_cancellation_policy.py",
"test_casualty_ground_policy.py",
"test_casualty_reservation_death.py",
"test_casualty_loading_policy.py",
"test_initial_guard_policy.py",
"test_headless_game_runner.py",
"test_vanilla_game_control.py",
"test_archive_game_control.py",
"test_site_discovery_policy.py",
"test_faction_catalog_scope.py",
"test_mission_rules.py",
"test_admin_setting_policy.py",
"test_player_admission_policy.py",
"test_progression_identity.py",
"test_discovery_map_position.py",
"test_local_admission.py",
"test_prison_placement_policy.py",
"test_recovery_policy.py",
"test_player_medical.py",
"test_time_policy.py",
"test_environment_policy.py",
"test_view_distance.py",
"test_environment_restart_runner.py",
"test_inventory_enumeration_policy.py",
"test_radio_defence_policy.py",
"test_radio_feedback_policy.py",
"test_performance_comparison.py",
"test_release_check.py",
"test_settings_migration.py",
"hq_network_regression.py",
"helicopter_availability_regression.py",
"mortar_salvo_regression.py"
],
"temporary_directory": "/tmp",
"results": [
{
"test": "test_native_package.py",
"started_at": "2026-10-01T23:43:07.570745+00:00",
"exit_code": 0,
"sha256": "0776a1eedede7185aa36c7fe816ea6231e6f475c0f6d210a2c732b23dc6d765e"
},
{
"test": "test_item_station_teardown.py",
"started_at": "2026-10-01T23:43:19.542833+00:00",
"exit_code": 0,
"sha256": "4e0f508dea6172862be82d6c7f295ede2bfb2412c390a0937a75ab176655f4b6"
},
{
"test": "test_source_bundle.py",
"started_at": "2026-10-01T23:43:19.842249+00:00",
"exit_code": 0,
"sha256": "074ad87ee7ab0b41ab94a7cb28efca66a975c24b8e2574122a13a52a4d491d0a"
},
{
"test": "test_native_flight_footprint.py",
"started_at": "2026-10-01T23:43:21.987930+00:00",
"exit_code": 0,
"sha256": "96923c018608f0c4d24294a9f8e769e2037be1fac8f1d51f4d73463706e134ad"
},
{
"test": "test_native_flight_input_diagnostics.py",
"started_at": "2026-10-01T23:43:22.349130+00:00",
"exit_code": 0,
"sha256": "355e2e45b86b9a46c68efb0563d16bef7a032cd7758169704b4b26ea92bee2bc"
},
{
"test": "test_mortar_logistics.py",
"started_at": "2026-10-01T23:43:22.468112+00:00",
"exit_code": 0,
"sha256": "98cdf38e43eb564163d8b409dfc95c4ae23e775c423651f05698acd52a7ef5a2"
},
{
"test": "test_mortar_courier_storage.py",
"started_at": "2026-10-01T23:43:23.151584+00:00",
"exit_code": 0,
"sha256": "0eb75fbcdcd712ff1c2220dcdd87cff6f3ed1e6e8f1c889916daa6b84a4359f5"
},
{
"test": "test_powered_flight_pad.py",
"started_at": "2026-10-01T23:43:23.480326+00:00",
"exit_code": 0,
"sha256": "690c1c6e05f3520a6d4d6c45a0fc5d9184e30a12b24356b3126bdb35c4171b82"
},
{
"test": "test_prison_escape_policy.py",
"started_at": "2026-10-01T23:43:23.801021+00:00",
"exit_code": 0,
"sha256": "d5d54588116eb730a8fda046f25fc4edc89a5345f85774cbc0f0a9b3adb85b98"
},
{
"test": "test_powered_flight_policy.py",
"started_at": "2026-10-01T23:43:24.281738+00:00",
"exit_code": 0,
"sha256": "86bfd5851be6fe3a187e1fe7fd2b7b0965c891cf29b2260b0df292255c5c547a"
},
{
"test": "test_flight_action_runner.py",
"started_at": "2026-10-01T23:43:26.181575+00:00",
"exit_code": 0,
"sha256": "aa14ebe85fce7c0435f48aceec1d4463b33fe8a5792b4e935b543dff94f4ad02"
},
{
"test": "test_native_local_admission_probe.py",
"started_at": "2026-10-01T23:43:27.308531+00:00",
"exit_code": 0,
"sha256": "16864bc359ab607dae3919442bd18f191320721411a8156e23be999f24e4454f"
},
{
"test": "test_mortar_report_ledger.py",
"started_at": "2026-10-01T23:43:27.419758+00:00",
"exit_code": 0,
"sha256": "fd2346e06c37a9e7621e1861e0f472b72ac14786fd2bcfbd785b62700d55ffde"
},
{
"test": "test_dedicated_native.py",
"started_at": "2026-10-01T23:43:27.803488+00:00",
"exit_code": 0,
"sha256": "f0323b20743221bd1ad46d6462246c2a0e2be84b395e40faa39007ef330aaa3c"
},
{
"test": "test_native_mortar_transfer.py",
"started_at": "2026-10-01T23:43:27.890563+00:00",
"exit_code": 0,
"sha256": "3b2a201a73c9ff2509276428988e4a98023c6aa94f8eeb42bc06c6cc604473d2"
},
{
"test": "test_mortar_aim_renewal.py",
"started_at": "2026-10-01T23:43:28.074500+00:00",
"exit_code": 0,
"sha256": "cf228b7f83d247d93277ec4c8644eb1090a20336a7e9ee2b90086c78199a70ca"
},
{
"test": "test_dedicated_preflight.py",
"started_at": "2026-10-01T23:43:28.456071+00:00",
"exit_code": 0,
"sha256": "0d654375db6b8eb91d0b92663b50e32776aaa7ecafbfdb83c00f04826df5236e"
},
{
"test": "test_mortar_transactions.py",
"started_at": "2026-10-01T23:43:28.559693+00:00",
"exit_code": 0,
"sha256": "b169cee3ccde42a24b6a0aa0ac2c3ff879bd1478bed1979dbea7b82c8006ee43"
},
{
"test": "test_native_mortar_probe.py",
"started_at": "2026-10-01T23:43:29.043685+00:00",
"exit_code": 0,
"sha256": "217801a6eddd48f76e99363a72a9812c0f48766ebd2faf8e188f4466ed1e4088"
},
{
"test": "test_diagnostic_no_splash.py",
"started_at": "2026-10-01T23:43:29.392546+00:00",
"exit_code": 0,
"sha256": "d99539a8b3277c2f1ac3e3bfa4c897876fe21e704c9ae33c542eed79fc2e9e65"
},
{
"test": "test_casualty_incapacitation.py",
"started_at": "2026-10-01T23:43:29.706225+00:00",
"exit_code": 0,
"sha256": "55b5cfb7b3eae496a8cc18888872d11331ecc01afb7326fbee3b8d65047dbe93"
},
{
"test": "test_casualty_remove_probe.py",
"started_at": "2026-10-01T23:43:29.810914+00:00",
"exit_code": 0,
"sha256": "e3b8ac386bb8e4a9832215cbbe1cacf7a5bd1314467bd4b07ed4a57b2db75458"
},
{
"test": "test_extraction_placement_policy.py",
"started_at": "2026-10-01T23:43:29.920777+00:00",
"exit_code": 0,
"sha256": "d924f163d98c78989938ca01eca5935891fb454266810f4817cbe49651b84ae2"
},
{
"test": "test_guard_cancellation_policy.py",
"started_at": "2026-10-01T23:43:30.033634+00:00",
"exit_code": 0,
"sha256": "f8f134b6d525a3c775677b2a95c562b12aa84ba287318c08c833d684ad0bd9d8"
},
{
"test": "test_casualty_ground_policy.py",
"started_at": "2026-10-01T23:43:30.538505+00:00",
"exit_code": 0,
"sha256": "6543c594293d7122242ec68d506a92dc81464d110cdb7496a225555cee338c83"
},
{
"test": "test_casualty_reservation_death.py",
"started_at": "2026-10-01T23:43:30.631693+00:00",
"exit_code": 0,
"sha256": "fd68dce04ea48ee2a22fac499c32a5c53847a25e3f14eaba3f50437d2c040f25"
},
{
"test": "test_casualty_loading_policy.py",
"started_at": "2026-10-01T23:43:31.049295+00:00",
"exit_code": 0,
"sha256": "cdf2c0abe4b794502f18228b173db47c4093ea062378ec9e3971e0c4f9e3bf1f"
},
{
"test": "test_initial_guard_policy.py",
"started_at": "2026-10-01T23:43:31.156966+00:00",
"exit_code": 0,
"sha256": "0c80e744db1ce0a0d0746c43d867f5d2f47d9b92bc48d6561ceeb28a204d8743"
},
{
"test": "test_headless_game_runner.py",
"started_at": "2026-10-01T23:43:31.252423+00:00",
"exit_code": 0,
"sha256": "0d84160076ec9961e7315ccd50d6ce45ff971328c298649505fbba6305c22f31"
},
{
"test": "test_vanilla_game_control.py",
"started_at": "2026-10-01T23:43:31.351381+00:00",
"exit_code": 0,
"sha256": "8ab3777a5bd0f8213efae08bd6db70a05790af992c2abfda8e5e5aa079beebbb"
},
{
"test": "test_archive_game_control.py",
"started_at": "2026-10-01T23:43:31.430442+00:00",
"exit_code": 0,
"sha256": "8126e2be91f9ee8c09fceb82d03bb0d9c51247909189a424c3e24dd2b1ed22ce"
},
{
"test": "test_site_discovery_policy.py",
"started_at": "2026-10-01T23:43:32.335104+00:00",
"exit_code": 0,
"sha256": "40fda37b86d469a1cf8fcb7a1fb18abcaab0df44ad09f50018744d42f8f4f649"
},
{
"test": "test_faction_catalog_scope.py",
"started_at": "2026-10-01T23:43:32.431601+00:00",
"exit_code": 0,
"sha256": "c8fab92575f2621f47ed8685243338aa9ea717bb809c2ef9e8e0884af9df263b"
},
{
"test": "test_mission_rules.py",
"started_at": "2026-10-01T23:43:32.483947+00:00",
"exit_code": 0,
"sha256": "f534ae447fe1db6ae939430c3b14568d2bf1b84956dca295cce04211411d9ee7"
},
{
"test": "test_admin_setting_policy.py",
"started_at": "2026-10-01T23:43:32.586596+00:00",
"exit_code": 0,
"sha256": "69af6f74e262ee4981ac428ac1ab85561f8b365aed5879e20d315ededca9ee22"
},
{
"test": "test_player_admission_policy.py",
"started_at": "2026-10-01T23:43:32.885082+00:00",
"exit_code": 0,
"sha256": "745ec0a9e491ffcf5bec53ebf67ecee8333f4dcfa5d19bafbde31cbd1cad7f8b"
},
{
"test": "test_progression_identity.py",
"started_at": "2026-10-01T23:43:33.113410+00:00",
"exit_code": 0,
"sha256": "2fb3725aa735e00b20ba7a4ac45f8e087d4346c58a176bee091bab0a9755c1df"
},
{
"test": "test_discovery_map_position.py",
"started_at": "2026-10-01T23:43:33.215919+00:00",
"exit_code": 0,
"sha256": "0644aea60d4c546e819ef417771563ab7598e84d5e13db2fa63416ae1fe6ddb0"
},
{
"test": "test_local_admission.py",
"started_at": "2026-10-01T23:43:33.427367+00:00",
"exit_code": 0,
"sha256": "0bde82f6d5c31c4187ed0aa1935940b8b9e634868b3a4820eca073580ad136b4"
},
{
"test": "test_prison_placement_policy.py",
"started_at": "2026-10-01T23:43:33.690051+00:00",
"exit_code": 0,
"sha256": "34539744426f0b30e41883203484ed60606df8ca319324014f07e833bcbf854b"
},
{
"test": "test_recovery_policy.py",
"started_at": "2026-10-01T23:43:34.318945+00:00",
"exit_code": 0,
"sha256": "adc4966ab92f0849d08636d93da5bd02b9162d1ad7369b325a1a357903abaa55"
},
{
"test": "test_player_medical.py",
"started_at": "2026-10-01T23:43:34.451856+00:00",
"exit_code": 0,
"sha256": "e4b3140a3afc0161db53f261e5480e51f1ca09f85657e25b0efb7dbba3336e3f"
},
{
"test": "test_time_policy.py",
"started_at": "2026-10-01T23:43:35.390641+00:00",
"exit_code": 0,
"sha256": "45ae5768f6e87009b4074937cbc2e384c59341e905a3f250672bd2a110b2e6c5"
},
{
"test": "test_environment_policy.py",
"started_at": "2026-10-01T23:43:35.767985+00:00",
"exit_code": 0,
"sha256": "66b86e66f47c4ce397c6093a50a724bbe8dedbf3db53e0dd7d53ff33243a0b52"
},
{
"test": "test_view_distance.py",
"started_at": "2026-10-01T23:43:36.251164+00:00",
"exit_code": 0,
"sha256": "ddc6cbeb41485f14a4f2ce23a7a86f1708e7e335bbeecee6309be49fd72e00e7"
},
{
"test": "test_environment_restart_runner.py",
"started_at": "2026-10-01T23:43:36.737802+00:00",
"exit_code": 0,
"sha256": "05bc09bc45db70ba4a635b65b0a96aa38437ba50c98640b47ed8d0683c3a5f71"
},
{
"test": "test_inventory_enumeration_policy.py",
"started_at": "2026-10-01T23:43:37.359486+00:00",
"exit_code": 0,
"sha256": "95cbfd7c4351cb9a8e1cd6783524be79f77255e5996299919a61dcfce8b0f21a"
},
{
"test": "test_radio_defence_policy.py",
"started_at": "2026-10-01T23:43:37.770202+00:00",
"exit_code": 0,
"sha256": "48c9c01dc6135a3f6d2187b0974645758040f041c3c2c2c26b14222fc572dfe1"
},
{
"test": "test_radio_feedback_policy.py",
"started_at": "2026-10-01T23:43:37.888904+00:00",
"exit_code": 0,
"sha256": "6b576472f81a35bd4df4dae3f19925b046eedbba262ad67f9bfe1ce9d220a487"
},
{
"test": "test_performance_comparison.py",
"started_at": "2026-10-01T23:43:38.000368+00:00",
"exit_code": 0,
"sha256": "5f932de9e9898acdd379f26c777a35c884269522dbf8c41991a7a25bf61c369a"
},
{
"test": "test_release_check.py",
"started_at": "2026-10-01T23:43:40.317242+00:00",
"exit_code": 0,
"sha256": "8b6e755ed922ad129ba01c228989610a31f3a79841dc7cdddd4ddbd0e510971b"
},
{
"test": "test_settings_migration.py",
"started_at": "2026-10-01T23:43:41.316176+00:00",
"exit_code": 0,
"sha256": "5a82ba8672b72fb388a95e297d53452e45679ed3b94f5c7d4598455179038aef"
},
{
"test": "hq_network_regression.py",
"started_at": "2026-10-01T23:43:42.346967+00:00",
"exit_code": 0,
"sha256": "f7021b7bbdfdc2eefcdfade2bc4ad2108e1312cd07e8e09025783c626e7c8cae"
},
{
"test": "helicopter_availability_regression.py",
"started_at": "2026-10-01T23:43:43.284760+00:00",
"exit_code": 0,
"sha256": "3f256b7f5db246723b75caa2cc247249881cba95bde71ea1c90733404fe19f35"
},
{
"test": "mortar_salvo_regression.py",
"started_at": "2026-10-01T23:43:43.574442+00:00",
"exit_code": 0,
"sha256": "0f5d5f5d33f64ff43f5a2b954fe4eec032a00697f51f09efa10284537a3682db"
}
],
"scope": "Split continuation of original run; no engine launched; initial quota failure retained.",
"all_remaining_passed": true,
"initial_log_unchanged": true,
"resumed_log_sha256": "e63811819e3ef393f8c6a054698b38e7ebd8dea976fb8c70fb05d99e7ff91e91",
"explicit_prefix_registered": 6,
"explicit_prefix_success_evidence": [
{
"test": "tests/test_prefab_identity.py",
"initial_log_line": 1,
"success_output": "PASS: missing, child-only, malformed, zero and duplicate prefab root IDs rejected"
},
{
"test": "tests/test_spawn_scene.py",
"initial_log_line": 2,
"success_output": "PASS: delayed scene/inventory arrival, deferred preload, identity checks, stale replies, timeout and observer cleanup"
},
{
"test": "tests/test_spawn_scene_inventory.py",
"initial_log_line": 3,
"success_output": "PASS: actual inventory manifest, delayed/missing/wrong-owner item links, finite cap, consumed-stock refresh and preload ordering"
},
{
"test": "tests/test_spawn_scene_lease.py",
"initial_log_line": 4,
"success_output": "PASS: spawn scene remains held through final acknowledgement and nearby use; leaving, reassignment, disconnect and teardown release it"
},
{
"test": "tests/test_prison_ai.py",
"initial_log_line": 5,
"success_output": "PASS: production prison AI hold/release, repeat activation, authority/faction/player exclusion, dead actor and stop cleanup"
},
{
"test": "tests/test_native_player_death_fixture.py",
"initial_log_line": 6,
"success_output": "PASS: actual protected/capped damage plus exact-actor terminal fixture bypass through delayed native damage, fail-closed ownership and untouched ordinary actor; no native claim"
}
],
"loop_registered": 266,
"loop_passed_before_failed_test": 211,
"remaining_registered": 55,
"registered_order": [
"tests/test_prefab_identity.py",
"tests/test_spawn_scene.py",
"tests/test_spawn_scene_inventory.py",
"tests/test_spawn_scene_lease.py",
"tests/test_prison_ai.py",
"tests/test_native_player_death_fixture.py",
"tests/test_native_maintenance_refuel.py",
"tests/test_mine_intel.py",
"tests/test_mine_intel_discovery_integration.py",
"tests/test_infantry_combat.py",
"tests/test_enemy_medicine.py",
"tests/test_hunter_combat.py",
"tests/test_hq_local_awareness.py",
"tests/test_unconscious_controls.py",
"tests/test_town_garrisons.py",
"tests/test_field_sites.py",
"tests/test_gameplay_session_regressions.py",
"tests/test_gameplay_review_fixes.py",
"tests/test_server_log_regressions.py",
"tests/test_mortar_load_ack.py",
"tests/test_hunter_profiles.py",
"tests/test_hunter_lifecycle.py",
"tests/test_hunter_opening.py",
"tests/test_enemy_awareness.py",
"tests/test_intel_body_inspection.py",
"tests/test_body_recovery_lifecycle.py",
"tests/test_air_support_policy.py",
"tests/test_air_logistics_orders.py",
"tests/test_air_recon.py",
"tests/test_scouts.py",
"tests/test_mortar_station.py",
"tests/test_random_settings.py",
"tests/test_debug_ai.py",
"tests/test_generated_sites.py",
"tests/test_terrain_sites.py",
"tests/test_terrain_adapter.py",
"tests/test_terrain_airfields.py",
"tests/test_terrain_takeover.py",
"tests/test_faction_selection.py",
"tests/test_faction_menu.py",
"tests/test_content_policy.py",
"tests/test_hq_road_intercept.py",
"tests/test_hq_delivered_task.py",
"tests/test_hq_local_handoff.py",
"tests/test_hq_local_handoff_intake.py",
"tests/test_native_local_handoff_probe.py",
"tests/test_infantry_resupply_waypoints.py",
"tests/test_infantry_placement_settle.py",
"tests/test_native_infantry_fixture_lifecycle.py",
"tests/test_native_infantry_cargo.py",
"tests/test_native_infantry_setup.py",
"tests/test_infantry_ammo_access.py",
"tests/test_infantry_resupply_planning.py",
"tests/test_infantry_supply_pool.py",
"tests/test_hq_multiconvoy_supply.py",
"tests/test_enemy_gunner_skill.py",
"tests/test_native_memory_deadlines.py",
"tests/test_convoy_auto_settings.py",
"tests/test_convoy_startup.py",
"tests/test_failure_result_refresh.py",
"tests/test_infantry_resupply_coordinator.py",
"tests/test_infantry_mine_coordinator.py",
"tests/test_infantry_mine_transfer.py",
"tests/test_infantry_ammo_transfer.py",
"tests/test_finite_magazine_reservation.py",
"tests/test_mounted_ammo_transfer.py",
"tests/test_mounted_ammo_pickup.py",
"tests/test_mounted_resupply.py",
"tests/test_mounted_supply_integration.py",
"tests/test_hq_ammo_reporting.py",
"tests/test_enemy_radio.py",
"tests/test_native_maintenance_stock.py",
"tests/test_packed_external_game.py",
"tests/test_hq_radio_node.py",
"tests/test_vehicle_maintenance_stock.py",
"tests/test_optional_transport.py",
"tests/test_vehicle_entry_diagnostics.py",
"tests/test_vehicle_security_native_probe.py",
"tests/test_vehicle_security.py",
"tests/test_vehicle_alarm.py",
"tests/test_vehicle_alarm_perception.py",
"tests/test_native_convoy_mine_stock.py",
"tests/test_native_mine_stock_runner.py",
"tests/test_native_mounted_ammo_probe.py",
"tests/test_native_mounted_swap_probe.py",
"tests/test_native_mounted_courier_probe.py",
"tests/test_native_alarm_hearing.py",
"tests/test_native_convoy_cover_probe.py",
"tests/test_enemy_optics.py",
"tests/test_convoy_combat.py",
"tests/test_ai_stinger_admin.py",
"tests/test_hq_ammo_dispatch.py",
"tests/test_hq_convoy_combat.py",
"tests/test_hq_ammo_sampler.py",
"tests/test_hq_mine_sampler.py",
"tests/test_hq_mounted_ammo_sampler.py",
"tests/test_hq_mounted_binding.py",
"tests/test_hq_vehicle_seats.py",
"tests/test_native_hq_body_probe.py",
"tests/test_hq_delivered_casualties.py",
"tests/test_external_profile_review.py",
"tests/test_hq_uplink_fairness.py",
"tests/test_actor_group.py",
"tests/test_native_package_external.py",
"tests/test_native_convoy_probe.py",
"tests/test_convoy_placement.py",
"tests/test_convoy_road_plan.py",
"tests/test_hq_convoy_dispatch.py",
"tests/test_convoy_admission.py",
"tests/test_convoy_dispatch.py",
"tests/test_convoy_lifecycle.py",
"tests/test_convoy_budget.py",
"tests/test_supply_convoys.py",
"tests/test_supply_budget_admission.py",
"tests/test_mounted_initial_stock.py",
"tests/test_convoy_mine_stock.py",
"tests/test_convoy_column.py",
"tests/test_hq_body_observer.py",
"tests/test_hq_status_sampler.py",
"tests/test_hq_watch.py",
"tests/test_hq_assignment_lifecycle.py",
"tests/test_force_ledger.py",
"tests/test_enemy_equipment.py",
"tests/test_enemy_loadout.py",
"tests/test_automatic_equipment.py",
"tests/test_engineer_mine.py",
"tests/test_engineer_mine_site.py",
"tests/test_engineer_mine_hq.py",
"tests/test_hq_doctrine.py",
"tests/test_hq_coverage.py",
"tests/test_hq_task_reservations.py",
"tests/test_hq_pursuit.py",
"tests/test_hq_memory.py",
"tests/test_external_addons.py",
"tests/test_admin_tick_delivery.py",
"tests/test_project_schema.py",
"tests/test_admin_session.py",
"tests/test_admin_providers.py",
"tests/test_admin_menu.py",
"tests/test_journal_readability.py",
"tests/test_admin_player_tools.py",
"tests/test_intel_admin.py",
"tests/test_intel_reroll.py",
"tests/test_intel_discovery_feedback.py",
"tests/test_hq_report_selection.py",
"tests/test_casualty_feedback.py",
"tests/test_initial_admission_exhaustion.py",
"tests/test_prison_held_action_proof.py",
"tests/test_prison_held_action_evidence.py",
"tests/test_prison_held_input_adapter.py",
"tests/test_prison_held_runner.py",
"tests/test_prison_coarse_facing.py",
"tests/test_aircraft_field_probe.py",
"tests/test_aircraft_field_runner.py",
"tests/test_aircraft_field_rules.py",
"tests/test_aircraft_field_envelope.py",
"tests/test_aircraft_field_flight.py",
"tests/test_command_maintenance.py",
"tests/test_aircraft_service.py",
"tests/test_aircraft_service_cancel_runner.py",
"tests/test_prison_action_selection_proof.py",
"tests/test_prison_action_selection_evidence.py",
"tests/test_ordinary_gate_choices.py",
"tests/test_aircraft_control_bridge.py",
"tests/test_prison_alignment_proof.py",
"tests/test_prison_alignment_evidence.py",
"tests/test_aircraft_route_demand.py",
"tests/test_prison_approach_proof.py",
"tests/test_prison_approach_evidence.py",
"tests/test_ordinary_gate_evidence.py",
"tests/test_ordinary_gate_observer.py",
"tests/test_ordinary_look_observer.py",
"tests/test_ordinary_look_evidence.py",
"tests/test_relative_look_runner.py",
"tests/test_radio_waiting_text.py",
"tests/test_inventory_control_runner.py",
"tests/test_inventory_layout.py",
"tests/test_headless_input_proof.py",
"tests/test_headless_input_exercise_runner.py",
"tests/test_private_prefix_cleanup.py",
"tests/test_runner_private_cleanup.py",
"tests/test_ordinary_input_runner.py",
"tests/test_ordinary_input_observer.py",
"tests/test_aircraft_route_envelope.py",
"tests/test_aircraft_route_policy.py",
"tests/test_aircraft_route_probe.py",
"tests/test_rough_intel_feedback.py",
"tests/test_headless_display_probe.py",
"tests/test_headless_display_runner.py",
"tests/test_preparation_status.py",
"tests/test_hint_delivery.py",
"tests/test_native_end_lifecycle.py",
"tests/test_native_end_runner.py",
"tests/test_escape_result_wording.py",
"tests/test_native_stats.py",
"tests/test_aircraft_translation.py",
"tests/test_spawn_retry_budget.py",
"tests/test_spawn_preload_timeout.py",
"tests/test_action_feedback.py",
"tests/test_native_aircraft_control.py",
"tests/test_aircraft_flight_policy.py",
"tests/test_aircraft_flight_envelope.py",
"tests/test_ordinary_screenshot.py",
"tests/test_participant_selection.py",
"tests/test_native_shared_map_ownership.py",
"tests/test_shared_map_ownership.py",
"tests/test_intel_registration.py",
"tests/test_mortar_activity_completion.py",
"tests/test_packed_chat_game.py",
"tests/test_native_mortar_refresh.py",
"tests/test_archive_packed.py",
"tests/test_mortar_refresh.py",
"tests/test_packed_core_game.py",
"tests/test_pak_inventory.py",
"tests/test_native_mortar_salvo.py",
"tests/test_spectator_guidance.py",
"tests/test_spectator_recovery.py",
"tests/test_native_package.py",
"tests/test_item_station_teardown.py",
"tests/test_source_bundle.py",
"tests/test_native_flight_footprint.py",
"tests/test_native_flight_input_diagnostics.py",
"tests/test_mortar_logistics.py",
"tests/test_mortar_courier_storage.py",
"tests/test_powered_flight_pad.py",
"tests/test_prison_escape_policy.py",
"tests/test_powered_flight_policy.py",
"tests/test_flight_action_runner.py",
"tests/test_native_local_admission_probe.py",
"tests/test_mortar_report_ledger.py",
"tests/test_dedicated_native.py",
"tests/test_native_mortar_transfer.py",
"tests/test_mortar_aim_renewal.py",
"tests/test_dedicated_preflight.py",
"tests/test_mortar_transactions.py",
"tests/test_native_mortar_probe.py",
"tests/test_diagnostic_no_splash.py",
"tests/test_casualty_incapacitation.py",
"tests/test_casualty_remove_probe.py",
"tests/test_extraction_placement_policy.py",
"tests/test_guard_cancellation_policy.py",
"tests/test_casualty_ground_policy.py",
"tests/test_casualty_reservation_death.py",
"tests/test_casualty_loading_policy.py",
"tests/test_initial_guard_policy.py",
"tests/test_headless_game_runner.py",
"tests/test_vanilla_game_control.py",
"tests/test_archive_game_control.py",
"tests/test_site_discovery_policy.py",
"tests/test_faction_catalog_scope.py",
"tests/test_mission_rules.py",
"tests/test_admin_setting_policy.py",
"tests/test_player_admission_policy.py",
"tests/test_progression_identity.py",
"tests/test_discovery_map_position.py",
"tests/test_local_admission.py",
"tests/test_prison_placement_policy.py",
"tests/test_recovery_policy.py",
"tests/test_player_medical.py",
"tests/test_time_policy.py",
"tests/test_environment_policy.py",
"tests/test_view_distance.py",
"tests/test_environment_restart_runner.py",
"tests/test_inventory_enumeration_policy.py",
"tests/test_radio_defence_policy.py",
"tests/test_radio_feedback_policy.py",
"tests/test_performance_comparison.py",
"tests/test_release_check.py",
"tests/test_settings_migration.py",
"tests/hq_network_regression.py",
"tests/helicopter_availability_regression.py",
"tests/mortar_salvo_regression.py"
],
"all_registered_covered": true,
"accounting_note": "Six explicit prefix subprocess tests passed in original log before the 266-test loop. Original run passed prefix 6 plus loop 211 before quota failure; resumed canonical-/tmp run passed remaining 55, including native_package. Total coverage 272 registered subprocess tests; original quota failure preserved."
}
@@ -0,0 +1,254 @@
RESUME test_native_package.py
................usage: test_native_package.py [-h] [--project {core}] --session SESSION
--compat COMPAT [--steam STEAM]
[--timeout TIMEOUT]
[--external-addons EXTERNAL_ADDONS]
[--silent-workbench | --exit-after-init]
[--continue-on-failure]
test_native_package.py: error: argument --exit-after-init: not allowed with argument --silent-workbench
................................
----------------------------------------------------------------------
Ran 48 tests in 11.854s
OK
RESUME test_item_station_teardown.py
.......
----------------------------------------------------------------------
Ran 7 tests in 0.243s
OK
RESUME test_source_bundle.py
..............
----------------------------------------------------------------------
Ran 14 tests in 2.058s
OK
RESUME test_native_flight_footprint.py
....
----------------------------------------------------------------------
Ran 4 tests in 0.305s
OK
RESUME test_native_flight_input_diagnostics.py
....
----------------------------------------------------------------------
Ran 4 tests in 0.059s
OK
RESUME test_mortar_logistics.py
PASS: actual finite-courier age, token, exact ownership/callback and delivered-status admission reject stale, replayed, captured, canceled and ambiguous work
PASS: actual native-gun ownership gate rejects player/foreign/replaced/dead occupant, faction change, missing seat and missing registry
PASS: four-battery courier selection preserves pending trip/report/transfer identity, skips unavailable crews, and selects the next depleted battery without new stock
RESUME test_mortar_courier_storage.py
PASS: 64 bounded distinct storage candidates; native gates, five-store barrier and finite-stock ownership retained; native placement pending
RESUME test_powered_flight_pad.py
PASS: 128 coarse+80 local refinement+169 airport grid candidates retain all native clearance gates; real terrain untested
RESUME test_prison_escape_policy.py
PASS: actual prison registration/crossing policies reject missing/duplicate/destroyed initial gate, incomplete ownership/scan, outside-only body, jumps and stale samples; physical exit independent of later gate damage
PASS: actual prison door execution blocks direct AI/UI bypass while locked and delegates unchanged native action once after unlock and permission approval
RESUME test_powered_flight_policy.py
..................
----------------------------------------------------------------------
Ran 18 tests in 1.839s
OK
RESUME test_flight_action_runner.py
.....
----------------------------------------------------------------------
Ran 5 tests in 1.066s
OK
RESUME test_native_local_admission_probe.py
PASS: native local-admission snapshot rejects changed life/key/body/participant/pending state; authentication is not simulated
RESUME test_mortar_report_ledger.py
PASS: actual mortar report ledger prevents duplicate response after cancellation, expiry and interleaved reports; native scheduler separate
RESUME test_dedicated_native.py
.........
----------------------------------------------------------------------
Ran 9 tests in 0.012s
OK
RESUME test_native_mortar_transfer.py
....
----------------------------------------------------------------------
Ran 4 tests in 0.135s
OK
RESUME test_mortar_aim_renewal.py
PASS: actual aim renewal requires fresh same-report solve and original-command equivalence; native firing untested
RESUME test_dedicated_preflight.py
........
----------------------------------------------------------------------
Ran 8 tests in 0.013s
OK
RESUME test_mortar_transactions.py
PASS: actual startup/generation/ownership/timeout/callback policies and native-counter wait; integration preserves one-shot allocation and pending-crew cancellation
PASS: rapid mortar cycle counts eight physical rounds once and waits for native completion without a ninth aim or shell
RESUME test_native_mortar_probe.py
PASS: actual probe binds native events to fresh immutable commits, permits delayed emission, rejects unrelated expiry closure and premature stop
RESUME test_diagnostic_no_splash.py
PASS: actual noSplash Load body requires both explicit diagnostic and noSplash, preserves vanilla otherwise, and handles repeated Load/reload
RESUME test_casualty_incapacitation.py
PASS: actual fixture method permits one native resilience operation and restores either prior protection state, including failure paths
RESUME test_casualty_remove_probe.py
PASS: actual safe-exit boundary policy, native action/door selection, stop/cache cleanup, separate old mechanism and opt-in runner contracts
RESUME test_extraction_placement_policy.py
PASS: actual one-shot deadline/stability policies and preparation ownership, fuel, cleanup integration contracts
RESUME test_guard_cancellation_policy.py
PASS: pending-only cancellation retires copied instance intent, preserves physical/control actors, and stops once
RESUME test_casualty_ground_policy.py
PASS: actual short-cast hit, upward support normal, no self support and no dynamic support
RESUME test_casualty_reservation_death.py
PASS: actual committed-ticket structure preserves reservation after death, rejects missing bodies/seats and ends at observation expiry
PASS: actual pending callback expires an empty-server reservation once, preserves its full lease, and cancels on completion/Stop
RESUME test_casualty_loading_policy.py
PASS: rescue lease expiry/reordering, fresh five-second settling, independent rescue ranges and grounded upright aircraft motion boundaries
RESUME test_initial_guard_policy.py
PASS: requested guard population requires complete physical conscious membership before deadline
RESUME test_headless_game_runner.py
......................................
----------------------------------------------------------------------
Ran 38 tests in 0.017s
OK
RESUME test_vanilla_game_control.py
..........
----------------------------------------------------------------------
Ran 10 tests in 0.002s
OK
RESUME test_archive_game_control.py
.....
----------------------------------------------------------------------
Ran 5 tests in 0.826s
OK
RESUME test_site_discovery_policy.py
PASS: authored radio/optional-site bounds and physical horizontal/vertical boundaries
RESUME test_faction_catalog_scope.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
RESUME test_mission_rules.py
PASS: all phase transitions, terminal states, defence boundaries, stale/future observations, intel discovery
RESUME test_admin_setting_policy.py
PASS: Core document-discovery alias, companion-only alias exclusion, strict integer parsing and setting boundaries
RESUME test_player_admission_policy.py
PASS: player admission phase/life/identity/controller matrix, preload displacement limits, pending reservation overlap and owner exclusion
RESUME test_progression_identity.py
PASS: actual gate and pre-inventory document guards reject noncanonical/faction/life/disconnect/server mismatches; canonical reconnect and unconscious carried intel preserved
PASS: WB-only native fault injection restores both body mutations before checks/returns and requires eligible held-document negative/positive proof; runtime pending
RESUME test_discovery_map_position.py
PASS: actual discovery coordinates disclose stable 500m cell center until identified, then exact point, including negative/boundary coordinates
RESUME test_local_admission.py
PASS: actual local admission context/selection/latch, same-round late identity, replacement/mode failure, fresh round and untouched network path
RESUME test_prison_placement_policy.py
PASS: prison search packs 16 unique 3m-spaced inside-wall centers, prioritizes inner/edge positions and never bypasses clearance
PASS: physical body occupancy rejects overlap, permits diagonal neighboring slots, and excludes own body and unrelated broad-phase bounds
RESUME test_recovery_policy.py
PASS: recovery state excludes dead bodies, preserves reservations, requires controlled rescuers and distinguishes empty/all-dead teams
RESUME test_player_medical.py
PASS: authority-scoped slow recovery, bleeding pauses resilience, medicine unaffected, player-only lethal damage cap without healing
PASS: bleeding severity survives low health, retains armour, causes no knockout and restores the actual damage/blood-loss cap
PASS: viable passive recovery survives native delay; bleeding, disabled/full effects and low-blood resilience do not block defeat
RESUME test_time_policy.py
PASS: every minute round-trips, malformed clocks rejected, defaults limited to added keys, progression bounds and day duration aligned
RESUME test_environment_policy.py
PASS: native settings policy matches calendar boundaries 1900-2050, date/profile parsing is strict, authored defaults preserve old worlds
RESUME test_view_distance.py
PASS: view-distance bounds, default, save failures, command parsing, temporary ownership, server/platform limits and lifecycle wiring
RESUME test_environment_restart_runner.py
.....
----------------------------------------------------------------------
Ran 5 tests in 0.569s
OK
RESUME test_inventory_enumeration_policy.py
.
----------------------------------------------------------------------
Ran 1 test in 0.355s
OK
RESUME test_radio_defence_policy.py
PASS: radio authorization/discovery/phase matrix, exact distance limits, defender handover, interruption, empty samples, clock regression and duration boundary
RESUME test_radio_feedback_policy.py
PASS: actual radio feedback policy, initial wait, interruption persistence, resumption and terminal/new-round reset
RESUME test_performance_comparison.py
...............
----------------------------------------------------------------------
Ran 15 tests in 2.242s
OK
RESUME test_release_check.py
..............................................
----------------------------------------------------------------------
Ran 46 tests in 0.921s
OK
RESUME test_settings_migration.py
PASS: legacy values preserved, unsupported fields reported, invalid data rejected, existing files protected, runtime bounds aligned
PASS: failed initial settings promotion leaves defaults writable after restart; prior/fallback saves survive copy, readback and mismatch faults
RESUME hq_network_regression.py
PASS: real-carrier shortest-hop paths, 500m boundary, three-hop cap, no-manpack failure, three-second originating-call interruption, unchanged downlink hop timing, completed-hop survival, interrupted final uplink, radio loss, capture, motion, timeout, reset and network bounds
RESUME helicopter_availability_regression.py
PASS: migrated aircraft availability policy (controller migration still pending)
RESUME mortar_salvo_regression.py
PASS: migrated salvo offsets are deterministic, spread and bounded (native firing and complete salvo qualification are separate)
@@ -0,0 +1,521 @@
PASS: missing, child-only, malformed, zero and duplicate prefab root IDs rejected
PASS: delayed scene/inventory arrival, deferred preload, identity checks, stale replies, timeout and observer cleanup
PASS: actual inventory manifest, delayed/missing/wrong-owner item links, finite cap, consumed-stock refresh and preload ordering
PASS: spawn scene remains held through final acknowledgement and nearby use; leaving, reassignment, disconnect and teardown release it
PASS: production prison AI hold/release, repeat activation, authority/faction/player exclusion, dead actor and stop cleanup
PASS: actual protected/capped damage plus exact-actor terminal fixture bypass through delayed native damage, fail-closed ownership and untouched ordinary actor; no native claim
PASS: actual Tick clock initialization admits fractional first ticks and expires without extending the original start
PASS: actual finite conservation/deadline/callback policies; read-only selection, bounded once-only inventory submission and no can refill; native transfer not executed
PASS: actual exact-context membership, bounded identity/reach gates, native start/cache/eligibility ordering and no duplicate/foreign action start; fuel manager identity and empty-can headroom guards checked
PASS: actual detached node/value snapshot and conservation refuse mismatched lengths before indexed reads, retain original identities under native out-array aliasing and log bounded counts once; native cause remains unknown
PASS: actual mine evidence sealing/recovery, historical coarse publication, caps, canonical ownership, marker join retry/owner-change/stop; native arming and replicated vector-array transport untested
PASS: actual full document recovery retains authored/sealed targets, one shared spent ledger, retry/exhaustion semantics and canonical multi-player ownership alongside supplemental mine intel
PASS: actual infantry search suspension, quiet grace, finite resume, expiry/replacement and defensive/ownership boundaries
PASS: infantry combat gate precedes radio delivery and preserves non-task transports
PASS: actual HQ contact transition wakes only fit squad members, cancels only task orders and logs changes once
PASS: actual finite medicine grants, authored stock deduction, full-backpack refusal/pouch fallback, no loot replenishment, unresolved-operation quarantine and ownership/stop guards
PASS: only recent native detected/identified threats suspend hunter travel; stale/disarmed/destroyed contacts cannot strand patrols
PASS: actual hunter combat transition cancels only its travel once, holds through renewed contact and resumes after quiet; unregistered startup preserved
PASS: captured sighting retention, immutable details, expiry, replay rejection, bounded sound uncertainty/cooldown, radio loss, priority and overdue-status fairness
PASS: arrow press/release, held keys, opposing keys, disabled/menu input and firearm-only retention
PASS: town trigger and hard 700m exclusion for all observers, empty/invalid positions and final-check crossings
PASS: exact finite site squads, invalid roster/count rejection, committed-site spacing and safe fallback boundary
PASS: mortar couriers use the mobile site team and refuse permanent-only garrisons
PASS: omitted preferred mortar depot selects a surviving owned camp/mobile team; no camp, foreign cache and permanent-only sites are unavailable; historical priority retained
PASS: real-session mortar deadline, failed-support convoy admission and hunter survivor/recovery gates
PASS: native-route retry preserves delivered target, limits attempts and suspends during combat
PASS: stale/deleted AI utility cannot receive Complete; mortar HQ preflight withholds unsafe orders; mounted convoy cover never creates infantry Defend
PASS: flight crew affiliation and group precede native boarding; preparation remains bounded even while passengers wait
PASS: factionless/deleted map targets do not abort perception; faction lookup tolerates teardown; deleting helicopter releases without capture/engine access
PASS: shoot adapter waits for a physical round; bounded same-shell retries, interrupted/never-started loads, committed no-retry, order/owner changes and normal native delegation
PASS: missing/invalid faction crew defers before any native spawn or allowance insertion; all registered RHS factions have explicit soldier and medic profiles
PASS: vehicle radio issued once; pending, refusal and looting cannot duplicate or fabricate a physical radio link
PASS: actual vehicle-radio link requires the original seated crew, physical manpack and live uncaptured mission vehicle; looting, dismount, release and teardown remove it
PASS: actual hunter release preserves survivors/HQ, releases driving ownership once, delegates capture/teardown, and excludes prison/patrol/mortar from generic objective defence
PASS: real opening road geometry enforces wide azimuths; fresh delivered contact preempts patrol cooldown, respects radio failures and does not starve in-flight interception
PASS: actual scoped 10% tighter REGULAR aim error and alert perception; preserves equipment multiplier and restores vanilla behavior when unbound
PASS: physical inspection and parent-wreck retention; server-only searched/report delivery unlocks native cleanup once with a 300-second delay
PASS: bounded same-group empty-spawn retries, no casualty refill, queue retirement, delivery-gated intel, prepared-team reuse, throttled diagnostics and LOD release
PASS: production airborne memory confidence, persistence, ground coverage, confirmed losses, artillery veto, cooldown and invalid-input rules
PASS: production cover retains the carried HQ target until its deadline; airport reload waits until the drop pass and cover have finished
PASS: air logistics reservation preserves delivered search until radio handoff, excludes guards/combat/in-flight orders, and quarantines unresolved native loads/moves
PASS: actual ammunition cargo selector handles empty rifle/machinegun/sniper magazines, preserves loaded variants, excludes auxiliary launchers and tolerates missing native metadata
PASS: original 15-minute launch/loss cadence, active/captured timer disarm, saved delay reset, stop, 300m orbit and bounded close-contact standoff at 30/60/120 Hz
PASS: actual aircraft-removal notification guards owner/server/active/stop and schedules one full delay without replay extension
PASS: hill prominence/elevation, actual road edges/heights/bends and server-only finite scout admission, pending stock, timeout and no replacements/refills
PASS: mortar duty scope excludes players, other crews, stopped/client/inactive states; only destruction releases an active assignment, while the gunner may approach the gun
PASS: random mode restrictions, full numeric ranges, independent calendar dates and strict daylight boundaries
PASS: actual killais eligibility includes the 1000m boundary, excludes players/retained bodies/dead/non-character entities and checks full 3D distance
PASS: actual generation scheduler bounds each tick, enforces deadline/candidate ceiling, validates fallback, commits only accepted positions, skips only dispensable sites, preserves required objectives/intel, and resets the next search budget
PASS: terrain placement samples world bounds, retains streaming candidates, bounds waits/work, rejects foreign fallbacks, preserves objectives/intel and validates completion
PASS: actual navigation readiness supports static meshes, yields only for loadable streaming tiles and refuses unreachable positions
PASS: universal connector, embedded/standalone exports, multiple terrains, stable IDs, native nav configs, missing-nav visibility, path validation and overwrite protection
PASS: airfield detection accepts physical runways/towers and bounded authored markers, rejects unrelated props, water and out-of-world markers, and samples terrain height
PASS: configured airport parsing, bounded readiness, stale admission rejection and cleanup wiring
PASS: explicit CLI opt-in, disabled/editor guards and mission-header airport precedence
PASS: singleton identity guards, paired faction deserialization and client world-cleanup wiring
PASS: task/map/respawn getters survive delayed old destruction and preserve ordinary-mode behavior
PASS: replication hierarchy refuses absent/aliased nodes and establishes delivery dependencies idempotently
PASS: standalone old faction delegates collected once, with null/deleted owners ignored
PASS: actual faction resolution reserves explicit roles, samples without replacement, substitutes disabled selections, and rejects duplicate/unknown/missing choices and insufficient pools
PASS: Random all submits persistent random roles immediately; manual mixed choices, stale/unauthorized UI guards and debounce preserved
PASS: actual content policy gates parent packs, individual factions, faction identity and finite group rotation with vanilla fallback
PASS: actual delivered road eligibility, ahead projection without chords, reservations, stock road query bounds, terrain/water/obstruction refusal and clean outputs
PASS: actual delivered-task lineage, expiry, old-order retention, native commit, release, road precision expiry and duplicate delivery
PASS: actual original-roster local handoff,100m3D bound,busy carrier/budget exclusion,origin interruption including deletedsource,normal radio delivery and per-source replay history
PASS: actual visual intake retains source/time/position/id, rejects replay, prefers usable own route, excludes simultaneous callers and conservatively resets negative-sweep dwell
PASS: actual fixture local/radio gating, immutable source/time/position, interrupted-report exclusion and exact-owner cleanup
PASS: local-handoff runner refuses wrong project/round count/insufficient deadline before runtime work
PASS: actual fixture allocator terminal/unsubmitted guard, original-group lookup and both candidate gates
PASS: actual report-derived waypoint insertion/order and tighter positive radii; no formation navigation claim
PASS: actual Place/Stop waits native pose update, bounds four attempts, preserves original position, restores pending/settled placement and respects ownership/accepted-move quarantine
PASS: actual fixture current/original recipient ownership, unresolved transfer cleanup, exact-token release and idempotent Stop
PASS: actual fixture phased-cargo readiness, bounded callbacks, captured-stock mixed-caliber selection/continuation, reservation cleanup and component magazine counts
PASS: actual inventory-fixture setup uses ordinary prison unlock before constructing its convoy-waiting probe; failed unlock cannot run fixture
PASS: actual authored storage anchors, native trace flags, own-hierarchy exclusion, external wall/vehicle obstruction, range, unknown geometry and bounded context refusal
PASS: actual convoy binding, finite identities, disjoint tokens, shared report replay/cooldown, original recipients and HQ cleanup
PASS: actual finite convoy supply pool; immutable slots, shared demand state, sparse/capped discovery and permanent shutdown
PASS: actual HQ rotates finite donors, skips unavailable/contacted trucks, uses delivered reports and dispatches at most one trip per planning cadence
PASS: actual original turret identity, native/canonical guards, scoped maximum-skill profile, reset, dismount/reboard, requested-skill preservation and restoration
PASS: bounded HQ startup/memory windows and outcome-specific full-route deadlines
PASS: explicit escape-route skips only administrator stress, preserving original full-fixture wait/failure paths
PASS: normal escape route keeps ordinary catalog and generated placement; historical selectors remain unchanged
PASS: actual read migrates old valid settings, preserves explicit disabled state, rejects invalid data; next-round feedback and fixed menu remain honest
PASS: actual automatic convoy timing/authority/admission/one-attempt/manual-task/stop policy; native deployment unproved
PASS: late authoritative failure reason refreshes open result; detach, mode change and success subscriptions preserved
infantry resupply actual rules, radio transitions, owned cleanup and unresolved-operation tests passed
PASS: actual resupply Roster preserves permanent release, original slot mapping and bounded ownership
PASS: actual designated zero/one original carrier selection, settled startup, shared finite claims, no refund after submission
PASS: actual HQ startup grant readiness forwarding is authoritative and fail-closed
PASS: actual finite PMN4 designated-zero/one entitlement, pinned original ownership, shared commit, physical custody, callback quarantine/deletion and no retry
PASS: actual bounded empty-return and finite delivery; phase-isolated callbacks, exact ownership proof, stop quarantine and original actor/vehicle boundaries
PASS: actual shared finite identity reservation; competing consumers, submission permanence, stale cleanup and96-item bound
PASS: actual carried-mounted swap, shared committed claim, physical slot reach, exact two-way proof, uncertainty and terminal cleanup
PASS: actual mounted pickup, exact finite ownership, pre-submission commit, physical custody, bounded callback and quarantine lifecycle; no native coverage claim
PASS: actual mounted courier admission, radio, individual orders, two-stage transfer, reboarding, token cleanup and quarantine lifecycle
PASS: actual scoped crew permit and native board completion/failure preserves foreign seats without teleport fallback
PASS: actual shared mounted stock/history ownership, atomic exact HQ pair claim, and bounded stopped-owner proof polling
PASS: actual carried/mounted early ammunition policy and status-first shared uplink; complete threshold, phases, cooldown, failures requested check-ins and non-urgent detached carried-mine status
PASS: actual group radio startup preservation, finite native grants, callback quarantine, frozen selection/actor ownership and bounded observation
PASS: actual maintenance native-stock check rejects missing/duplicate/wrong-identity/unowned/depleted/malformed stock and is read-only; native runtime not executed
..................
----------------------------------------------------------------------
Ran 18 tests in 0.295s
OK
PASS: actual radio node rejects current incapacitation, death, ownership and faction changes despite cached inventory; one-second capability refresh remains explicit
PASS: Southern finite maintenance stock preserves weapons/clues and optional transport, historical Western fixture stays separate; native capacity/consumption untested
PASS: optional transport deferred behind required readiness, bounded once-only spawn/omission, isolated cleanup, occupied-candidate retention and required-default preservation
PASS: actual diagnostic fallback runs only on failed missing-context call, derives unique native pilot door, preserves stock eligibility and restores prior index on success/failure; diagnostics mutate no game state.
PASS: actual native-prelude cleanup restores only original changed damage, refuses stale/incapacitated/aboard teleport; bounded five-seat/four-door fixture wiring checked. Native execution remains pending.
PASS: actual CreateTransport requires successful scoped Configure after handbrake, preserves ordinary vehicles, tracks spawned entity before failures and never treats failed lock registration as ready.
PASS: actual preparation Configure guards/idempotence/breach preservation, security eligibility/final current-body server mutation and all scoped action guards; all five seat/four door resource overrides. Native hold, prefab merge, replication and boarding remain unverified.
PASS: production vehicle alarm single trigger/replica completion/no replay, fixed deadline/readiness wait, failed audio, dedicated server, moving exact handle, destruction/native end/deletion/clock and mission teardown. Native sound and multiplayer delivery require engine qualification.
PASS: production alarm emitter/receiver original time/location, explicit native queue only, 32-entry cap, 50m local witness eligibility, bound perception, native-super preservation; actual engine broadcast/hearing remains unverified.
PASS: actual optional candidate barrier, exact callback/custody proof and unchanged mine/primary snapshots; no native transfer claim
Native finite mine-stock runner: parser, scope, exclusions and command pass
PASS: actual bounded native mounted proof/detached payload/deadline/cleanup methods, production-only setup and isolated runner selector
PASS: actual one-existing-convoy candidate readiness, sender-root identity, incomplete cargo refusal and fixed three-candidate bound
PASS: actual once-only native dry query, exact before/after slots, native refusal, shutdown; shared bounded discovery and distinct opt-in verdict
PASS: actual completion selector and pre-cleanup variant capture preserve separate radio and eligibility verdicts
PASS: actual cargo diagnostic stages separate absent/foreign magazines, used/empty/invalid rounds, well mismatch and exact truck storage/attachment without mutation
PASS: actual native courier witness rejects missing radio/custody/swap/reboard evidence; CLI opt-in bounded; native success remains unqualified
PASS: actual courier constructor/Tick waits for preparation, returns before physical checks, preserves fixed deadline and sticky stop/failure; Enfusion startup awaits native verification
PASS: actual arranged native alarm fixture deadline, single emit, natural-route observation, original timestamp/area/detail assertions, natural observer proximity/activation wait, failure cleanup and independent canonical-body restoration; native delivery itself remains unqualified.
PASS: actual convoy cover observation/snapshot/uplink/HOLD/RESUME/cleanup transitions; native boundaries mocked, non-convoy runner opt-in refused
Enemy optics actual-service ownership, preservation, callback, deadline and bounded-inspection policies passed
PASS: actual delivered cover freshness/lineage/replay and canonical executor; owned waypoint-only hold, one resume at preserved route index, invalid ownership/crew/ledger refusal and fail-closed native recreation
PASS: actual Stinger show/set/help state precise actor-roll effects; read-only help, bounded values, malformed syntax and failed-save preservation; no rerolls/gameplay edits and no native persistence claim
PASS: new/retasked search and radio delivery ammo gates; active tasks and delivered release preserved
HQ convoy delivered-report planning and downlink regressions passed
PASS: actual own-inventory ammo inspection deduplicates loaded/carried/shared wells, separates chambers/launchers, freezes scalar snapshots and treats unsupported/capped/foreign inventory as unknown
PASS: actual carried PMN4 inventory, original-owner guards, bounded deduplication, independent unknown state and detached counts
PASS: actual mounted hierarchy sampling, ownership exclusion, deduplication, completeness/caps, quarter threshold and detached validation; no runtime policy or ammunition mutation
PASS: WORKBENCH opt-in diagnostics distinguish cycles/cap/storage/manager/mount failures without repeated native traversal
PASS: actual HQ original convoy-root admission rejects wrong force/server/group and never supplies replacement lookup
PASS: production mounted structural seat scan ignores >128 carried/cargo descendants, shares exact native mount/magazine ownership, preserves attachment seats and exact occupants, rejects unknown/cyclic/oversize structures; native binding still requires qualification.
PASS: native body fixture uses existing immutable actors, real production LOS/queued uplink, pre-delivery isolation, actual status overlap, bounded cleanup and no fabricated reports/spawns
PASS: actual native fixture lookup selects exact BODY kind/identity/message from bounded evaluations, never sighting-only Get
PASS: actual ammo snapshot comparison checks every scalar/category flag; native probe requires real roster rescan, loaded-round lower bound and separate immutable pending/Watch objects
PASS: actual delivered slot-union bodies preserve overlap/disjoint evidence, replay idempotence, status subset integrity, full-wipe closure and formation-local bounds
test_review_added_allowed_media_invalidates_snapshot (__main__.ReviewTests.test_review_added_allowed_media_invalidates_snapshot) ... ok
test_review_added_source_directory_rejected (__main__.ReviewTests.test_review_added_source_directory_rejected) ... ok
test_review_loose_root_resource_and_symlink_rejected (__main__.ReviewTests.test_review_loose_root_resource_and_symlink_rejected) ... ok
test_review_metadata_media_and_sidecars_remain_allowed_and_bound (__main__.ReviewTests.test_review_metadata_media_and_sidecars_remain_allowed_and_bound) ... ok
test_review_missing_link_rejected (__main__.ReviewTests.test_review_missing_link_rejected) ... ok
test_review_package_guard_sees_new_scripts (__main__.ReviewTests.test_review_package_guard_sees_new_scripts) ... ok
test_review_retargeted_private_link_rejected (__main__.ReviewTests.test_review_retargeted_private_link_rejected) ... ok
----------------------------------------------------------------------
Ran 7 tests in 0.074s
OK
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
PASS: actual actor group lookup retains controlled character membership when seated/inherited vehicle agents differ; missing control or parent refuses
.................
----------------------------------------------------------------------
Ran 17 tests in 3.747s
OK
PASS: actual convoy motion thresholds, native-only proof boundaries, physical cargo/crew ownership and pre-launch runner restrictions
PASS: actual filled rotated-footprint TraceBox and nine terrain/water samples; broad tree/cable bounds alone accepted, actual collision/nonfinite trace refused, nonzero sweep and ENTS/OCEAN flags, bounded diagnostic query and reset
PASS: actual road interpolation/projection/orientation, six35m starts,200m forward tail, preserved bends, short/far/degenerate/nonfinite/overdense/hairpin refusal and deterministic candidate ties
PASS: actual authenticated convoy dispatch, active/ready admission gates, canonical receipt, safe unconsumed preflight retry, pool refusal, retained partial failure and consumed no-retry
PASS: actual native readiness exact membership, one-time per-vehicle ledger admission/reservation, partial readiness retry without duplicate records and one-pass56-item cargo allocation without replenishment
PASS: actual per-vehicle readiness reasons, native agent/activity counts, intended-seat diagnostics, five-second throttle and read-only observation
PASS: actual convoy dispatch validates before receipt consumption, consumes once before native mutation, retains all12 partial-failure paths, accepts the selected non-USSR enemy, and allocates exact19 bodies without capacity shrinking
PASS: own memberless prefab disables automatic spawn before initialization; actual admission rejects populated, immediate or authored-slot templates before finite crew creation
PASS: actual native seated-group arrival outside35m, native waypoint removal, idempotent advance, and convoy waypoint idempotence/arrival/deadline, missing/dead/captured/reparented crew refusal, failed native waypoint release, original vehicle/member preservation and no replacement
PASS: actual convoy receipt identity/one-use, shared60-body and128-formation caps, outstanding/native-ledger accounting, exact19-body composition, source reuse, mode/client guards and no consumed receipt refunds
PASS: actual supply route cycling/arrival/stops, finite startup bounds, contact freshness, dismount hold, quiet interval, interrupted boarding, timeout and lost-truck release
PASS: moving-convoy perception boundaries and scoped individual/group abandonment priorities
PASS: actual supply startup reasons expose unchanged body budget/requirement, journal only failure changes, preserve retry cadence/caps and report the true final refusal
PASS: actual StartOne plus actual pool preserve unconsumed receipt retry, reject consumed replay, skip impossible new-budget surveys, retain equality, retry caps and journal dedup; no FPS claim
PASS: actual admission seeds use exact installed loaded prefab, reject replacement/foreign/projectile paths and preserve bounded immutable identity
PASS: actual primary-before-mounted one-shot batch, shared96/128 caps, physical snapshot headroom, original ownership, callback refusal and admission pinning
PASS: actual bounded HQ query validates only existing original binding without admission or replacement lookup
PASS: exact optional PMN4 startup, primary callback independence, original owner,128 capacity/callback limits, terminal no-retry and callback-proven freeze
PASS: production convoy spacing, physical arrival, stable follower targets, supply ownership, leader promotion and bounded stuck recovery
PASS: convoy combat keeps native manoeuvres for unchanged contacts and refreshes when contact moves
PASS: native convoy proof excludes pre-departure movement and ends via explicit fixture reset, not prohibited player death
PASS: boarding retries only after approach/seat progress stops, capped at three, with delayed native reissue and completed-team preservation
PASS: slow only owned vehicles near fit dismounted infantry; ignore passengers, casualties and released teams/vehicles
PASS: restore missing native driving registration only for healthy owned seated crews; preserve existing association and refuse invalid/released vehicles
PASS: actual body observer enforces owned physical roster, local death-only candidates, native trace gating, budget/cadence/caps, immutable body-only snapshots, radio retries and confirmed suppression
PASS: own initial roster, explicit retained corpse evidence, unconscious/missing/ref transfer guards, fixed strength, no replacement adoption, detached samples, surviving radio sender, immutable convoy root/ledger, mounted capture release and lost-root unknown
PASS: delivered-only status snapshots, two missed check-ins/reply deadline, last reported coordinates, episode/replay guards, confirmed-loss idempotence, detached/validated mounted payload replacement 256-record cap and detached carried-mine radio delivery/chronology
PASS: actual failed-retask deadline rollback plus assignment cancellation/release preserves radio ownership, native waypoint lifecycle and failed-order retry boundaries
PASS: actual force ledger, finite casualties/identity/cap, routes/arrival/elevation/overshoot, server and physical guards, staggered 256-record simulated hour without world queries
PASS: actual equipment coordinator registration dedupe, startup delay, five member scans, finite component retries, unregister/stop terminal behavior and 256-group bound
PASS: actual percentage selection, native/preferred launcher preservation, failure/removal fallback, pending-operation quarantine, capture, finite two-mine grants and no replenishment
PASS: automatic inventory one-shot roll, exact item ownership, finite compatible ammo, rejection, capture/stop, deletion, loot and late callback quarantine
PASS: enabled/valid catalog eligibility, category restrictions, content filtering, GUID deduplication across factions and bounded catalogs
PASS: actual engineer mine preparation/deadline, original custody/identity, once-only callback removal, cancellation quarantine, withdrawal/clearance, native refusal and owned-action cleanup; native execution untested
PASS: actual road mine-site annulus/bend geometry, deterministic bounded candidates, server/query limits, and native ground/water/obstruction rejection; no runtime placement claim
PASS: actual HQ mine admission, one site query per tick, maximum two active/32 retained jobs, delivered-only original post, once-per-unit refusal latch, 256-item ceiling and maintenance/teardown ownership
PASS: actual objective heading/threat/approach rules, persistent doctrine weight, fresh-contact coverage invalidation, stale-sweep rejection and atomic radio retask ownership
PASS: actual bounded delivered-negative coverage, replay/freshness/expiry/invalid guards, oldest eviction, small-circle and nearest-unswept selection, reserved-cell exclusion and cleanup
PASS: actual HQ active/pending reservations, interruption, committed replacement, bounded coverage and unchanged force counts
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 HQ memory rules/service preserve delivered snapshots, chronology, replay bounds, decay, motion limits, identity and negative-search revisions
.......
----------------------------------------------------------------------
Ran 7 tests in 0.031s
OK
PASS: actual session Tick + journal router delivers each grant completion and new HQ line once, without repeats on empty/unavailable feeds or unauthorized ticks
.....
----------------------------------------------------------------------
Ran 5 tests in 0.430s
OK
PASS: actual current-controller/admin/mode authorization, per-round anti-replay, token-rotation refusal, command/snapshot throttles and client scope; owner-only replies and player-tool cleanup wiring
PASS: mission provider registration is idempotent, optional dispatch is ordered, status aggregates, journals preserve cursors, aliases stay with their addons and topic help is bounded
PASS: actual menu command routing covers all fourteen settings, rejects unsupported keys, gates companions, and matches unique authored widgets
PASS: explicit weapon retention buttons use the same ready/authorized server command path
PASS: actual summaries preserve queued/delivered meaning, clear counts and IDs; raw records, legacy display, cursor gaps and RPC limits remain intact
PASS: actual admin body authorization, god restoration, vanilla item deposit, callback completion/failure, timeout quarantine and stop/revocation paths
PASS: same-body revoke/regrant preserves pending insertions and timeout quarantine; new-body binding isolates late callbacks and never inherits god mode
PASS: actual Intel commands distinguish status/start/failure, preserve authorization gate and reject malformed requests; spent ledger is idempotent, server-only and bounded
PASS: actual reroll snapshot, authority, cancellation, ownership, accepted operations, quarantine, bounded work and stop methods; no native runtime claim
PASS: production Intel feedback coalescing, rough secrecy, rate limit, bounded dedupe, local/remote delivery and round cleanup; native UI/RPC not exercised
PASS: actual per-squad bounded report selector rotates visible targets, survives ordering/turnover, records only queued attempts, and preserves radio intake ordering
PASS: actual action denial/clearing, server request failure, single submitted-result feedback, current rescuer identity and terminal/stop suppression; native UI/RPC pending
PASS: actual physical denial precedence and transport-seat occupancy/reservation/access/lock checks
PASS: actual initial admission exhaustion and ServerTick grace; new/pending/empty/controller/life guards, normal death preserved
......................
----------------------------------------------------------------------
Ran 22 tests in 0.270s
OK
......................
----------------------------------------------------------------------
Ran 22 tests in 27.080s
OK
..................
----------------------------------------------------------------------
Ran 18 tests in 3.048s
OK
.........
----------------------------------------------------------------------
Ran 9 tests in 2.389s
OK
...............
----------------------------------------------------------------------
Ran 15 tests in 1.219s
OK
PASS: actual field result/braking observer and Chat dispatch rules; one finite fixed candidate, deferred callback failure and guarded cleanup; no native measurement
PASS: actual opt-in terrain audit records one fixed1681-point grid, preserves original survey refusal/success and exact no-allocation scope; malformed heights stop without retry; no native data acquired
....
----------------------------------------------------------------------
Ran 4 tests in 0.683s
OK
PASS: actual field PI/ramp/vector caps, antiwindup, braking hypothesis, immutable admission limits and segment bounds; no native physics tested
PASS: actual100m whole-field survey sampling/4096cap, rotated coverage, all-node terrain and native trace refusal; original certificate lineage retained; no native qualification
PASS: actual QualifyField preserves original identity/footprint/time, orders complete surveys before issuance, and refuses failed stages/moved body/expired or changed world; native boundaries faked
PASS: WORKBENCH first-refusal diagnostics preserve ordinary predicates, query order/count/budget and opt-in scope; no native logging run
PASS: actual field startup delegation, immutable lease/target, braking/settling/cancel transitions, early abort, callback scope, physical terminal policy and shared landing demand; native boundaries faked
PASS: actual inherited airborne pre-CLIMB cancellation enables verified native autohover once, refuses failed enable and never writes after capture
PASS: actual Core maintenance/default/tick/stop methods; empty/populated authority, terminal reentry, no tactical resume or new scheduler; native boundaries substituted
PASS: actual finite registration, unique claims/tokens, retention/cancel/stop, readiness gating, capture/expiry retirement and bounded cross-kind order history and typed field/route construction; native boundaries faked
PASS: actual WORKBENCH route observer returns exact retained route subtype and refuses absent/field controllers
.....
----------------------------------------------------------------------
Ran 5 tests in 0.704s
OK
......................
----------------------------------------------------------------------
Ran 22 tests in 0.203s
OK
................
----------------------------------------------------------------------
Ran 16 tests in 10.963s
OK
......
----------------------------------------------------------------------
Ran 6 tests in 0.019s
OK
PASS: actual production bridge lookup, lease/world/mode/seat/capture guards, no allocation, native super ordering and read-only fixture hooks
...............
----------------------------------------------------------------------
Ran 15 tests in 0.054s
OK
.............
----------------------------------------------------------------------
Ran 13 tests in 5.219s
OK
PASS: actual route gain/caps/braking, callback selection, old landing/cancellation control and matching read-only telemetry
..............................
----------------------------------------------------------------------
Ran 30 tests in 0.441s
OK
..............
----------------------------------------------------------------------
Ran 14 tests in 0.217s
OK
.........
----------------------------------------------------------------------
Ran 9 tests in 0.116s
OK
PASS: actual gate observer binds native registered action/context and current life, reads accumulated progress, refuses stale bindings and never revives outside original bounds
PASS: actual read-only look observer methods enforce explicit scope, pinned native camera/body/identity, normalized pose, terminal cancellation and original sample/time bounds
...........
----------------------------------------------------------------------
Ran 11 tests in 0.075s
OK
.......................
----------------------------------------------------------------------
Ran 23 tests in 0.038s
OK
PASS: actual initial WAITING task/guide copy, configured duration, unchanged interruption and rounded progress; native task assertion pending
........
----------------------------------------------------------------------
Ran 8 tests in 0.013s
OK
PASS: actual inventory adapter scope, grid placement, sole compatible host, refresh reuse, rollback, foreign hierarchy refusal and exactly-once native delegation; native compatibility pending
.................................................................................
----------------------------------------------------------------------
Ran 81 tests in 0.558s
OK
......
----------------------------------------------------------------------
Ran 6 tests in 0.088s
OK
.................
----------------------------------------------------------------------
Ran 17 tests in 0.005s
OK
...........
----------------------------------------------------------------------
Ran 11 tests in 0.088s
OK
.........
----------------------------------------------------------------------
Ran 9 tests in 0.368s
OK
PASS: actual observer nonce, admission pins, baseline/menu sequencing, snapshots, bounded clocks/samples, cleanup and one-attempt latch; native untested
PASS: actual corridor caps/height/hold and whole-rectangle survey bounds, terrain refusal, native trace refusal and exact certificate lineage; native route unqualified
PASS: actual route entry, fixed corridor/target, hover hold, cancellation landing freeze, shared lease/capture/deadline gate and unchanged native startup/tick sequence
PASS: finite opt-in route fixture, original native input order, meaningful physical-arrival evidence and guarded cleanup; no native launch
PASS: actual Chat route step result dispatch and scoped completion/cleanup wiring; six real early-parser refusal paths leave no prefix/profile output
PASS: actual retained-service fixture entry, immutable controller/token/deadline/count bindings, observer getter and cleanup ownership ordering; native route validation pending
PASS: actual measured cancellation gate, one-shot retained service Stop, no premature drain, immutable lease, fresh real controller readback and separate physical cancellation result policy
PASS: actual rough-intel objective selection, exact precedence, source grid dimensions and unchanged other-phase dispatch; native UI pending
.................................
----------------------------------------------------------------------
Ran 33 tests in 0.103s
OK
...............
----------------------------------------------------------------------
Ran 15 tests in 0.035s
OK
PASS: actual preparation status scope, persistent updates, spinner restoration, placeholder replacement/deletion/destruction, native observation persistence/timeout; rendering remains native-pending
PASS: actual guide/observer monitors retry native refusal, cache successful delivery, deduplicate and preserve living/preparing/spectator boundaries; native UI not exercised
PASS: actual native-end shutdown, existing terminal snapshots, reentrancy, unchanged native end data and stale preparation guards
....
----------------------------------------------------------------------
Ran 4 tests in 1.525s
OK
PASS: actual three-state result wording and UI refresh; stale winner IDs never manufacture escape
...........
----------------------------------------------------------------------
Ran 11 tests in 0.123s
OK
PASS: actual immutable target, bounded autohover demand, drift refusal, five-second hold and unchanged qualification/deadline
PASS: actual admission attempt budget bounds blocked/missing prerequisites; preparation, spent life and async pending remain protected
PASS: delayed admission retry retains controller ownership across same-account recycled connection IDs
PASS: actual scene deadline overrides delayed native preload only for rejection; late completion cannot assign, ordinary verified admission preserved
PASS: actual action permission matrix unchanged; useful phase/intel/consciousness denials, stale reason cleared, native door denial preserved; no native UI claim
.....
----------------------------------------------------------------------
Ran 5 tests in 0.276s
OK
PASS: actual aircraft cancellation/stage rules, ground-only completion, expiry/capture faults, input ownership and no physical shortcuts
PASS: actual exact-instance single-use qualification, native-clock staleness/movement refusal, footprint bounds and ownership-safe ground/fault cleanup
PASS: actual optional screenshot helper flag/identity/world/life revalidation, one queued attempt, delayed capture and mission/world cleanup; no native image/presentation claim
PASS: actual yard diagnostic exact registered prison ownership, eight native-bound corner projections, no root-sibling escape, bounded duplicate/deep/wide hierarchy traversal; native projection/mask still unmeasured
PASS: actual CurrentActor, SpawnPosition and observer-selection methods reject pending/unregistered/replaced/recycled/foreign/dead bodies; canonical selection/cycling, unconscious following and prison/geometry fallback preserved; native boundaries adapted
PASS: actual native-map fixture ownership, cleanup and callback evidence fail closed; native execution remains separate
PASS: actual shared-map callbacks bind exact map/config/root, preserve replacement UI/camera, defer focus, and remove only owned delayed work
PASS: actual Intel/HQ methods and one-shot qualified readiness handoff deliver late actors once; duplicate readiness, accepted spawn, pickup/drop, death and original negative rolls do not mint assignments
PASS: Workbench-only read accessor rejects unknown/nonserver identities; Chat observes all distinct ready crew before existing shell-depletion mutation
PASS: actual native-event latch survives deselection, requires exact activity/order, detaches before cleanup, preserves failure/timeout and never infers completion from physical shot count
.........
----------------------------------------------------------------------
Ran 9 tests in 0.110s
OK
PASS: actual refresh-fixture gates pin immutable observations/actual downlinks/original activity, reject unaccepted or relabelled commits, stop queueing early and require all queued contacts claimed before replay
..........................
----------------------------------------------------------------------
Ran 26 tests in 0.219s
OK
PASS: actual refresh scheduler/delivery/cancellation/projectile methods reject interrupted or replayed reports, retain committed ownership, preserve immutable snapshots/aim geometry and isolate stopped or replaced orders; native radio/crew behavior remains separate
.....................
----------------------------------------------------------------------
Ran 21 tests in 0.039s
OK
...........
----------------------------------------------------------------------
Ran 11 tests in 0.038s
OK
PASS: actual full-salvo gates require eight fresh committed physical launches, natural closure and exact finite inventory; reject partial/extra/replayed events
PASS: actual reason/availability guidance and refresh matrix; no-target copy omits cycling, hint retry retained, existing command/admin paths unchanged; native UI pending
PASS: actual client camera policy closes immediately on healing or mission end; late unconscious metadata cannot keep the view active
PASS: actual input dispatch, one press per edge, menu-held suppression; actual revival camera handoff with editor close/retry, missing camera, replacement body and stopped round
PASS: first-person pose survives missing samples without drift, follows body motion/rotation, resumes fresh pose and cannot leak between targets
..........EE/usr/lib/python3.14/tempfile.py:962: ResourceWarning: Implicitly cleaning up <TemporaryDirectory '/tmp/tmp7pyk4hmn'>
_warnings.warn(warn_message, ResourceWarning)
.E...usage: test_native_package.py [-h] [--project {core}] --session SESSION
--compat COMPAT [--steam STEAM]
[--timeout TIMEOUT]
[--external-addons EXTERNAL_ADDONS]
[--silent-workbench | --exit-after-init]
[--continue-on-failure]
test_native_package.py: error: argument --exit-after-init: not allowed with argument --silent-workbench
................................
======================================================================
ERROR: test_cleanup_diagnostic_refuses_uncertain_exit_or_cleanup (__main__.NativePackageTests.test_cleanup_diagnostic_refuses_uncertain_exit_or_cleanup) (change={'cleanup_report': {'cleanup_confirmed': True, 'idle_verified': False, 'errors': []}})
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/jakub/devel/escapistsrereintroduced/tests/test_native_package.py", line 392, in test_cleanup_diagnostic_refuses_uncertain_exit_or_cleanup
code, summary, calls = self.orchestration_double([self.cleaned_report(**change)])
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/test_native_package.py", line 320, in orchestration_double
code = n.run(args, self.source)
File "/home/jakub/devel/escapistsrereintroduced/tools/native_package.py", line 500, in run
authored = copy_payload(root, session)
File "/home/jakub/devel/escapistsrereintroduced/tools/native_package.py", line 172, in copy_payload
with destination.open('xb') as output:
~~~~~~~~~~~~~~~~^^^^^^
OSError: [Errno 122] Disk quota exceeded
======================================================================
ERROR: test_cleanup_diagnostic_refuses_uncertain_exit_or_cleanup (__main__.NativePackageTests.test_cleanup_diagnostic_refuses_uncertain_exit_or_cleanup) (change={'cleanup_report': {'cleanup_confirmed': True, 'idle_verified': True, 'errors': ['problem']}})
----------------------------------------------------------------------
OSError: [Errno 122] Disk quota exceeded
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/jakub/devel/escapistsrereintroduced/tests/test_native_package.py", line 392, in test_cleanup_diagnostic_refuses_uncertain_exit_or_cleanup
code, summary, calls = self.orchestration_double([self.cleaned_report(**change)])
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/test_native_package.py", line 315, in orchestration_double
external_addons=self.external_fixture())
~~~~~~~~~~~~~~~~~~~~~^^
File "/home/jakub/devel/escapistsrereintroduced/tests/test_native_package.py", line 43, in external_fixture
fixture.setUp()
~~~~~~~~~~~~~^^
File "/home/jakub/devel/escapistsrereintroduced/tests/test_external_addons.py", line 24, in setUp
(folder/'addon.gproj').write_text(manifest)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
File "/usr/lib/python3.14/pathlib/__init__.py", line 809, in write_text
with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OSError: [Errno 122] Disk quota exceeded
======================================================================
ERROR: test_continuation_cannot_override_cleanup_runner_source_or_completion_failure (__main__.NativePackageTests.test_continuation_cannot_override_cleanup_runner_source_or_completion_failure) (change={'completion_observed': 'native-or-launcher-exited-prefix-active'})
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/jakub/devel/escapistsrereintroduced/tests/test_native_package.py", line 360, in test_continuation_cannot_override_cleanup_runner_source_or_completion_failure
code, summary, calls = self.orchestration_double([self.completed_report(**change)])
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/test_native_package.py", line 320, in orchestration_double
code = n.run(args, self.source)
File "/home/jakub/devel/escapistsrereintroduced/tools/native_package.py", line 500, in run
authored = copy_payload(root, session)
File "/home/jakub/devel/escapistsrereintroduced/tools/native_package.py", line 172, in copy_payload
with destination.open('xb') as output:
~~~~~~~~~~~~~~~~^^^^^^
OSError: [Errno 122] Disk quota exceeded
----------------------------------------------------------------------
Ran 48 tests in 13.021s
FAILED (errors=3)
PASS: 35 prefabs have unique saved root entity IDs
PASS: 299 original script entries tracked across 18 unfinished responsibilities
PASS: 1 isolated addons, acyclic dependencies, 56 unique resources, exact vanilla/authorized external references
Traceback (most recent call last):
File "/home/jakub/devel/escapistsrereintroduced/tools/check.py", line 79, in <module>
if __name__=='__main__':check()
~~~~~^^
File "/home/jakub/devel/escapistsrereintroduced/tools/check.py", line 77, in check
subprocess.run([sys.executable,str(ROOT/'tests'/name)],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 '['/usr/bin/python3', '/home/jakub/devel/escapistsrereintroduced/tests/test_native_package.py']' returned non-zero exit status 1.
@@ -0,0 +1,45 @@
{
"implemented": "Finite zero-to-one PMN4 replacement for original startup-designated engineers using fresh complete delivered mine reports; physical actor/mode identity and original stock retained.",
"constraints": [
"No mines generated or startup lottery rerolled",
"Unknown inventory is not empty",
"No submitted item retry/refund",
"Unresolved operations retain cross-truck claims",
"No EngineerMineAttempted reset or repeated laying authorization"
],
"independent_review": true,
"focused_regression_programs": 6,
"regression_programs_passed": 272,
"regression_execution": "Initial six explicit programs plus 211 successful loop programs; native_package interrupted by temporary quota, then recovered and all 55 remaining programs passed using canonical /tmp. Initial failure preserved.",
"native_evidence": "native-2026-10-02-zero-mine-integration",
"native_assertions": 63,
"native_rounds": 1,
"native_script_failures": 0,
"retained_installed_resource_errors": 13,
"native_qualification": "Frozen current-source compilation and scripted normal escape integration only; does not execute zero-mine rendezvous, transfer or placement.",
"cleanup_confirmed": true,
"preservation": {
"original_missions": {
"files": 533,
"unchanged": true
},
"playtest_candidate": {
"files": 409,
"unchanged": true
},
"inherited_unstaged": {
"files": 61,
"preserved": true,
"staging": "Only before-working to current diff applied atop unchanged captured index."
}
},
"playtest_package_updated": false,
"release_qualified": false,
"remaining": [
"Physical mine walking/transfer/arming",
"Human escape and real multiplayer",
"Physical maintenance and mounted transfers",
"Equivalent-workload FPS",
"Clean release/package qualification"
]
}
@@ -0,0 +1 @@
PASS: actual equipment coordinator registration dedupe, startup delay, five member scans, finite component retries, unregister/stop terminal behavior and 256-group bound
@@ -0,0 +1 @@
PASS: actual percentage selection, native/preferred launcher preservation, failure/removal fallback, pending-operation quarantine, capture, finite two-mine grants and no replenishment
@@ -0,0 +1 @@
PASS: actual group radio startup preservation, finite native grants, callback quarantine, frozen selection/actor ownership and bounded observation
@@ -0,0 +1,2 @@
PASS: actual designated zero/one original carrier selection, settled startup, shared finite claims, no refund after submission
PASS: actual HQ startup grant readiness forwarding is authoritative and fail-closed
@@ -0,0 +1 @@
PASS: actual finite PMN4 designated-zero/one entitlement, pinned original ownership, shared commit, physical custody, callback quarantine/deletion and no retry
@@ -0,0 +1 @@
PASS: actual convoy binding, finite identities, disjoint tokens, shared report replay/cooldown, original recipients and HQ cleanup