using System.Diagnostics;
using System.Collections.Concurrent;
using System.Text.Json;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Execution;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Planning;
using XFEEDToolBox.Core.Session;
using XFEEDToolBox.NativeSupport;
internal static class TradePauseRecoveryAcceptance
{
private sealed record WorkerStatus(DateTimeOffset At,string Message);
// No automatic menu opener is inferred from the old relog capability. The
// selected client must actually enter its original Pause during this window.
internal static async Task RunAsync(int pid,string directory,int seconds=45,bool openVerifiedPause=false)
{
if(seconds is <10 or >60)throw new ArgumentOutOfRangeException(nameof(seconds));
directory=Path.GetFullPath(directory);if(Directory.Exists(directory))throw new IOException("Use a new pause-acceptance evidence directory.");
Directory.CreateDirectory(directory);
try{await RunCoreAsync(pid,directory,seconds,openVerifiedPause);}
catch(Exception error)
{
var result=Path.Combine(directory,"result.json");
if(!File.Exists(result))await File.WriteAllTextAsync(result,JsonSerializer.Serialize(new{passed=false,pid,failure=error.ToString(),stage="before-running",openVerifiedPause,
flightInputsSent=0,tradeOrdersSent=0,stationServiceMutationsSent=0,osInputsSent=0,gameTerminationRequested=false},ToolboxSettings.JsonOptions));
throw;
}
}
private static async Task RunCoreAsync(int pid,string directory,int seconds,bool openVerifiedPause)
{
using var deadline=new CancellationTokenSource(TimeSpan.FromSeconds(seconds));var token=deadline.Token;
var settingsDirectory=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox");
var settings=ToolboxSettings.Load(Path.Combine(settingsDirectory,"settings.json"));
await using var journal=new JournalMonitor(settings.JournalDirectory);await journal.StartAsync(token);
using var game=Process.GetProcessById(pid);var gameStarted=game.StartTime.ToUniversalTime().Ticks;var gamePath=game.MainModule?.FileName;
if(game.ProcessName!="EliteDangerous64"||string.IsNullOrWhiteSpace(gamePath))throw new InvalidDataException("Exact existing game identity required.");
var pipe=new NavigationPipeClient(pid);var hash=NativeSystemLocationReader.GameSha256;
var hello=await pipe.CallAsync(new{op="describe"},token);
if(hello.GetProperty("processId").GetInt32()!=pid||hello.GetProperty("gameSha256").GetString()!=hash)throw new InvalidDataException("Build-pinned game identity required.");
var initial=journal.State;
if(!initial.InGame||!initial.Docked||initial.CurrentShipId is null||!initial.CargoReliable||initial.CargoCount!=0||initial.Credits is not >0||!initial.CreditsReliable)
throw new InvalidOperationException("This non-mutation acceptance requires the actual empty healthy docked ship and known wallet.");
var channel=new NativeCommerceChannel(pid);var initialInventory=await channel.CallAsync(new{op="commerce.inventory"},token);
var initialRaw=await pipe.CallAsync(new{op="read"},token);
if(initialRaw.GetProperty("shipHealth").GetProperty("emergency").GetBoolean())throw new InvalidOperationException("Existing health emergency was preserved; this is not a maintenance override.");
var catalogue=new NativeCurrentSystemSource(pid,hash,()=>journal.State);using var catalogueLifetime=catalogue;
var initialLocations=await catalogue.ReadLocationsAsync(token);
var repository=new MarketRepository(Path.Combine(settingsDirectory,"markets.db"));
var book=repository.LoadRegion(initial.MarketId!.Value,150,80);
var range=initial.MaxJumpRange??initial.Ships.Single(value=>value.ShipId==initial.CurrentShipId).MaxJumpRange;
if(range is not >0)throw new InvalidOperationException("Actual current ship jump range is not known.");
var request=new PlannerRequest{StartMarketId=initial.MarketId.Value,Credits=initial.Credits.Value,ReserveCredits=Math.Max(initial.Rebuy,0),
CargoCapacity=initial.CargoCapacity,OccupiedCargo=initial.CargoCount,LadenJumpRangeLy=range.Value,JumpRangeIsEstimate=true,
CurrentPosition=initial.Position,CurrentMarketId=initial.MarketId,CurrentSystemName=initial.SystemName,MaxLegDistanceLy=150,SearchRadiusLy=150,
MaxLegs=1,Loop=false,MaxMarketAgeMinutes=10080,MaxArrivalDistanceLs=200000,MinimumProfitPerTon=0,CostPerJump=0,BeamWidth=8,CargoSearchNodes=128};
var plan=new TradeRoutePlanner().CalculateCandidates(book,request,1,token).SingleOrDefault()
??throw new InvalidOperationException("No actual feasible cached route is available; no synthetic trade intent was created.");
await File.WriteAllTextAsync(Path.Combine(directory,"actual-intent-plan.json"),JsonSerializer.Serialize(plan,ToolboxSettings.JsonOptions),token);
await using var protection=await FlightTestProtectionScope.ArmAsync(pid,automaticExit:false,exitForFailedHandoff:false,token:token);
async Task Check(CancellationToken cancellation)
{
cancellation.ThrowIfCancellationRequested();game.Refresh();var current=journal.State;
if(game.HasExited||game.StartTime.ToUniversalTime().Ticks!=gameStarted||game.MainModule?.FileName!=gamePath||protection.Stopped
||!current.InGame||!current.Docked||current.CurrentShipId!=initial.CurrentShipId||current.CommanderId!=initial.CommanderId
||current.LoadGameAt!=initial.LoadGameAt||current.MarketId!=initial.MarketId||current.CargoCount!=initial.CargoCount
||!current.CargoReliable||NavigationSafety.UnhealthyHull(current)||NavigationSafety.HasExternalThreat(current))
throw new InvalidOperationException("Real game, docked ship or safety identity changed.");
var health=await pipe.CallAsync(new{op="health.read"},cancellation);
if(!health.GetProperty("shipHealth").GetProperty("verified").GetBoolean()||health.GetProperty("shipHealth").GetProperty("emergency").GetBoolean())
throw new InvalidOperationException("Original current health guard refused production preflight.");
}
var workerStatuses=new ConcurrentQueue<WorkerStatus>();
void LogStatus(string message){workerStatuses.Enqueue(new(DateTimeOffset.UtcNow,message));Console.WriteLine(message);}
var navigation=new NativeTradeNavigation(pid,hash,()=>journal.State,NativeSupportPackage.Directory,()=>settings.NavigationFuelReserve,Check);
navigation.Status+=LogStatus;
Task ForbiddenTravel(Station station,Func<CancellationToken,Task> before,CancellationToken cancellation)
=>throw new InvalidOperationException("Acceptance does not permit a travel call.");
var production=new NativeTradeCommandDriver(channel,()=>journal.State,Path.Combine(directory,"operations"),ForbiddenTravel,Check)
{JournalDirectory=settings.JournalDirectory,RunningMenuRecoveryFactory=navigation.EnterRunningMenuRecovery};
production.Status+=LogStatus;
var barrier=new TradePauseMutationBarrier(production,production);
var runner=new TradeRunner(()=>journal.State,(market,key)=>book.Quotes.FirstOrDefault(value=>value.MarketId==market&&value.Commodity==key));
var progress=new List<RunProgress>();var progressLock=new object();
runner.Progress+=value=>{lock(progressLock)progress.Add(value);};journal.JournalEvent+=runner.Observe;journal.PlayerChanged+=runner.ObservePlayer;
var observer=new NativeTradePauseRecoveryChannel(pid,hash,Path.Combine(NativeSupportPackage.Directory,"DockingControl"));
await observer.PrepareAsync(token);
var originalReceipt=await observer.ReadAsync(token);var originalRequestId=originalReceipt.GetProperty("tradePauseRecovery").GetProperty("requestId").GetString();
using var frames=new StreamWriter(new FileStream(Path.Combine(directory,"frames.jsonl"),FileMode.CreateNew,FileAccess.Write,FileShare.Read)){AutoFlush=true};
Task? running=null;string? failure=null;JsonElement? positive=null,paused=null,resumed=null,stopped=null,finalInventory=null,finalRaw=null;
bool originalResumeConfirmed=false,pauseRevoked=false,resumeNewScope=false,stopRevoked=false,cargoUnchanged=false,walletUnchanged=false,targetUnchanged=false;
string? runningOwner=null,openRequestId=null;var pauseOpenActionsSent=0;JsonElement? pauseOpenReceipt=null;
try
{
running=runner.RunAsync(plan,ExecutionMode.NativeBridge,barrier,token);
await Task.WhenAny(running,barrier.FirstMutationHeld).WaitAsync(TimeSpan.FromSeconds(10),token);
if(runner.State!=RunState.Running||!barrier.FirstMutationHeld.IsCompleted)throw new InvalidOperationException("Production preflight did not reach a real Running mutation barrier.");
Console.WriteLine("Real TradeRunner Running; all Buy/Sell/Travel/Service calls are blocked. "+(openVerifiedPause?"One explicit verified original Pause entry is authorized.":"Waiting for an actual original Pause; no menu-opening input is sent."));
do
{
token.ThrowIfCancellationRequested();var ready=await observer.ReadAsync(token);var authorization=ready.GetProperty("tradePauseRecovery");
await frames.WriteLineAsync(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,stage="running-scope-ready",runnerState=runner.State,raw=ready}));
if(authorization.GetProperty("scopeActive").GetBoolean()&&runner.State==RunState.Running&&barrier.BlockedBuys>0&&barrier.BlockedServices>0)
{runningOwner=authorization.GetProperty("owner").GetString();break;}
if(running.IsCompleted||protection.Stopped)throw new InvalidOperationException("Real runner or health protection ended before an original menu scope became active.");
await Task.Delay(80,token);
}while(true);
if(openVerifiedPause)
{
// Live evidence: persistent075-0514j-production-01 recovery
// opened Pause on this exact game SHA. Current native dispatch
// revalidates flow/CSM and finds one actual graph state Pause.
var opener=new NativeRelogChannel(pid,hash,returnToMenuTrial:true);var openOwner=Guid.NewGuid().ToString("N");
openRequestId=Guid.NewGuid().ToString("N");
try
{
await opener.BeginAsync(openOwner,token);var frame=await opener.ReadAsync(openOwner,token);
if(runner.State!=RunState.Running||!frame.Stable||frame.State!="InGame"||DateTimeOffset.UtcNow-frame.ObservedAt>TimeSpan.FromMilliseconds(200))
throw new InvalidOperationException("One verified Pause entry requires the real Running current InGame scope.");
pauseOpenActionsSent++;await opener.SubmitAsync(openOwner,openRequestId,frame,"@pause",token);
var until=DateTimeOffset.UtcNow.AddSeconds(3);
do
{
var observed=await opener.ReadAsync(openOwner,token);
if(observed.LastRequestId==openRequestId&&observed.ActionApplied)
{pauseOpenReceipt=JsonSerializer.SerializeToElement(observed,ToolboxSettings.JsonOptions);break;}
if(observed.LastRequestId==openRequestId&&observed.ActionFailed)throw new InvalidOperationException("The original unique Pause graph action refused entry; it was not retried.");
if(DateTimeOffset.UtcNow>=until)throw new TimeoutException("Original Pause entry was not acknowledged; it was not retried.");
await Task.Delay(30,token);
}while(true);
}
finally{await opener.EndAsync(openOwner);}
}
while(true)
{
token.ThrowIfCancellationRequested();if(runner.State!=RunState.Running||protection.Stopped)throw new InvalidOperationException("The real trade runner or actual health protection stopped before pause recovery.");
var current=journal.State;
if(!current.InGame||!current.Docked||current.CommanderId!=initial.CommanderId||current.CurrentShipId!=initial.CurrentShipId
||current.LoadGameAt!=initial.LoadGameAt||current.MarketId!=initial.MarketId||!current.CargoReliable||current.CargoCount!=initial.CargoCount
||NavigationSafety.HasExternalThreat(current)||NavigationSafety.UnhealthyHull(current))throw new InvalidOperationException("The real docked session changed while observing Pause.");
var value=await observer.ReadAsync(token);await frames.WriteLineAsync(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,runnerState=runner.State,raw=value}));
var receipt=value.GetProperty("tradePauseRecovery");
if(receipt.GetProperty("applied").GetBoolean()&&receipt.GetProperty("requestId").GetString() is {Length:32}
&&receipt.GetProperty("requestId").GetString()!=originalRequestId&&receipt.GetProperty("owner").GetString()==runningOwner
&&receipt.GetProperty("scopeActive").GetBoolean()
&&value.GetProperty("state").GetString()=="InGame"&&value.GetProperty("nextState").GetString()=="InGame")
{positive=value;originalResumeConfirmed=true;break;}
await Task.Delay(80,token);
}
var owner=positive.Value.GetProperty("tradePauseRecovery").GetProperty("owner").GetString();
runner.Pause();paused=await observer.ReadAsync(token);pauseRevoked=!paused.Value.GetProperty("tradePauseRecovery").GetProperty("scopeActive").GetBoolean();
if(!pauseRevoked)throw new InvalidOperationException("User Pause did not immediately revoke the original menu scope.");
await Task.Delay(350,token);var pausedAgain=await observer.ReadAsync(token);
if(runner.State!=RunState.Paused||pausedAgain.GetProperty("tradePauseRecovery").GetProperty("scopeActive").GetBoolean())throw new InvalidOperationException("A delayed Running report reauthorized a paused trade run.");
runner.Resume();
do
{
token.ThrowIfCancellationRequested();resumed=await observer.ReadAsync(token);var scope=resumed.Value.GetProperty("tradePauseRecovery");
if(scope.GetProperty("scopeActive").GetBoolean()&&scope.GetProperty("owner").GetString()!=owner){resumeNewScope=true;break;}
await Task.Delay(80,token);
}while(true);
runner.Stop();await running.WaitAsync(TimeSpan.FromSeconds(4));stopped=await observer.ReadAsync(CancellationToken.None);
stopRevoked=runner.State==RunState.Stopped&&!stopped.Value.GetProperty("tradePauseRecovery").GetProperty("scopeActive").GetBoolean();
if(!stopRevoked)throw new InvalidOperationException("Stop did not revoke the real Running scope.");
finalInventory=await channel.CallAsync(new{op="commerce.inventory"},CancellationToken.None);finalRaw=await pipe.CallAsync(new{op="read"});
var finalLocations=await catalogue.ReadLocationsAsync();
cargoUnchanged=initialInventory.GetProperty("count").GetInt32()==finalInventory.Value.GetProperty("count").GetInt32()
&&journal.State.CargoReliable&&journal.State.CargoCount==initial.CargoCount&&journal.State.Inventory.SequenceEqual(initial.Inventory);
walletUnchanged=initialInventory.GetProperty("credits").GetInt64()==finalInventory.Value.GetProperty("credits").GetInt64();
targetUnchanged=initialLocations.Locations.Where(value=>value.Selected).Select(value=>value.Identity).Order().SequenceEqual(finalLocations.Locations.Where(value=>value.Selected).Select(value=>value.Identity).Order());
if(!cargoUnchanged||!walletUnchanged||!targetUnchanged||finalRaw.Value.GetProperty("controlApplied").GetBoolean()
||finalRaw.Value.GetProperty("shipHealth").GetProperty("emergency").GetBoolean()||finalRaw.Value.GetProperty("shipHealth").GetProperty("damageObserved").GetBoolean())
throw new InvalidOperationException("Final cargo, wallet, target, health or released control did not match.");
}
catch(Exception error){failure=error.ToString();}
finally
{
runner.Stop();if(running is not null)try{await running.WaitAsync(TimeSpan.FromSeconds(4));}catch(Exception error){failure??=error.ToString();}
journal.JournalEvent-=runner.Observe;journal.PlayerChanged-=runner.ObservePlayer;
await File.WriteAllTextAsync(Path.Combine(directory,"result.json"),JsonSerializer.Serialize(new{passed=failure is null&&originalResumeConfirmed&&pauseRevoked&&resumeNewScope&&stopRevoked&&cargoUnchanged&&walletUnchanged&&targetUnchanged,
failure,pid,gameStarted,gamePath,initialShip=initial.CurrentShipId,initial.CargoCount,initial.CargoCapacity,initialInventory,finalInventory,
originalResumeConfirmed,pauseRevoked,resumeNewScope,stopRevoked,cargoUnchanged,walletUnchanged,targetUnchanged,
positive,paused,resumed,stopped,finalRaw,progress,workerStatuses=workerStatuses.ToArray(),barrier.BlockedBuys,barrier.BlockedSells,barrier.BlockedTravels,barrier.BlockedServices,
tradeOrdersSent=TradePauseMutationBarrier.ForwardedMutations,flightInputsSent=0,stationServiceMutationsSent=0,pauseOpenActionsSent,openVerifiedPause,openRequestId,pauseOpenReceipt,
gameExited=game.HasExited,gameTerminationRequested=false,screenUse=false,osInputsSent=0,limitations="One safe docked ship and exact original Pause only. No financial, travel, service or outside-Running menu-resume claim."},ToolboxSettings.JsonOptions));
}
if(failure is not null)throw new InvalidOperationException("Bounded zero-mutation pause acceptance did not pass; see result.json.");
}
}
using System.Diagnostics;
using System.Collections.Concurrent;
using System.Text.Json;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Execution;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Planning;
using XFEEDToolBox.Core.Session;
using XFEEDToolBox.NativeSupport;
internal static class TradePauseRecoveryAcceptance
{
private sealed record WorkerStatus(DateTimeOffset At,string Message);
// No automatic menu opener is inferred from the old relog capability. The
// selected client must actually enter its original Pause during this window.
internal static async Task RunAsync(int pid,string directory,int seconds=45,bool openVerifiedPause=false)
{
if(seconds is <10 or >60)throw new ArgumentOutOfRangeException(nameof(seconds));
directory=Path.GetFullPath(directory);if(Directory.Exists(directory))throw new IOException("Use a new pause-acceptance evidence directory.");
Directory.CreateDirectory(directory);
try{await RunCoreAsync(pid,directory,seconds,openVerifiedPause);}
catch(Exception error)
{
var result=Path.Combine(directory,"result.json");
if(!File.Exists(result))await File.WriteAllTextAsync(result,JsonSerializer.Serialize(new{passed=false,pid,failure=error.ToString(),stage="before-running",openVerifiedPause,
flightInputsSent=0,tradeOrdersSent=0,stationServiceMutationsSent=0,osInputsSent=0,gameTerminationRequested=false},ToolboxSettings.JsonOptions));
throw;
}
}
private static async Task RunCoreAsync(int pid,string directory,int seconds,bool openVerifiedPause)
{
using var deadline=new CancellationTokenSource(TimeSpan.FromSeconds(seconds));var token=deadline.Token;
var settingsDirectory=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox");
var settings=ToolboxSettings.Load(Path.Combine(settingsDirectory,"settings.json"));
await using var journal=new JournalMonitor(settings.JournalDirectory);await journal.StartAsync(token);
using var game=Process.GetProcessById(pid);var gameStarted=game.StartTime.ToUniversalTime().Ticks;var gamePath=game.MainModule?.FileName;
if(game.ProcessName!="EliteDangerous64"||string.IsNullOrWhiteSpace(gamePath))throw new InvalidDataException("Exact existing game identity required.");
var pipe=new NavigationPipeClient(pid);var hash=NativeSystemLocationReader.GameSha256;
var hello=await pipe.CallAsync(new{op="describe"},token);
if(hello.GetProperty("processId").GetInt32()!=pid||hello.GetProperty("gameSha256").GetString()!=hash)throw new InvalidDataException("Build-pinned game identity required.");
var initial=journal.State;
if(!initial.InGame||!initial.Docked||initial.CurrentShipId is null||!initial.CargoReliable||initial.CargoCount!=0||initial.Credits is not >0||!initial.CreditsReliable)
throw new InvalidOperationException("This non-mutation acceptance requires the actual empty healthy docked ship and known wallet.");
var channel=new NativeCommerceChannel(pid);var initialInventory=await channel.CallAsync(new{op="commerce.inventory"},token);
var initialRaw=await pipe.CallAsync(new{op="read"},token);
if(initialRaw.GetProperty("shipHealth").GetProperty("emergency").GetBoolean())throw new InvalidOperationException("Existing health emergency was preserved; this is not a maintenance override.");
var catalogue=new NativeCurrentSystemSource(pid,hash,()=>journal.State);using var catalogueLifetime=catalogue;
var initialLocations=await catalogue.ReadLocationsAsync(token);
var repository=new MarketRepository(Path.Combine(settingsDirectory,"markets.db"));
var book=repository.LoadRegion(initial.MarketId!.Value,150,80);
var range=initial.MaxJumpRange??initial.Ships.Single(value=>value.ShipId==initial.CurrentShipId).MaxJumpRange;
if(range is not >0)throw new InvalidOperationException("Actual current ship jump range is not known.");
var request=new PlannerRequest{StartMarketId=initial.MarketId.Value,Credits=initial.Credits.Value,ReserveCredits=Math.Max(initial.Rebuy,0),
CargoCapacity=initial.CargoCapacity,OccupiedCargo=initial.CargoCount,LadenJumpRangeLy=range.Value,JumpRangeIsEstimate=true,
CurrentPosition=initial.Position,CurrentMarketId=initial.MarketId,CurrentSystemName=initial.SystemName,MaxLegDistanceLy=150,SearchRadiusLy=150,
MaxLegs=1,Loop=false,MaxMarketAgeMinutes=10080,MaxArrivalDistanceLs=200000,MinimumProfitPerTon=0,CostPerJump=0,BeamWidth=8,CargoSearchNodes=128};
var plan=new TradeRoutePlanner().CalculateCandidates(book,request,1,token).SingleOrDefault()
??throw new InvalidOperationException("No actual feasible cached route is available; no synthetic trade intent was created.");
await File.WriteAllTextAsync(Path.Combine(directory,"actual-intent-plan.json"),JsonSerializer.Serialize(plan,ToolboxSettings.JsonOptions),token);
await using var protection=await FlightTestProtectionScope.ArmAsync(pid,automaticExit:false,exitForFailedHandoff:false,token:token);
async Task Check(CancellationToken cancellation)
{
cancellation.ThrowIfCancellationRequested();game.Refresh();var current=journal.State;
if(game.HasExited||game.StartTime.ToUniversalTime().Ticks!=gameStarted||game.MainModule?.FileName!=gamePath||protection.Stopped
||!current.InGame||!current.Docked||current.CurrentShipId!=initial.CurrentShipId||current.CommanderId!=initial.CommanderId
||current.LoadGameAt!=initial.LoadGameAt||current.MarketId!=initial.MarketId||current.CargoCount!=initial.CargoCount
||!current.CargoReliable||NavigationSafety.UnhealthyHull(current)||NavigationSafety.HasExternalThreat(current))
throw new InvalidOperationException("Real game, docked ship or safety identity changed.");
var health=await pipe.CallAsync(new{op="health.read"},cancellation);
if(!health.GetProperty("shipHealth").GetProperty("verified").GetBoolean()||health.GetProperty("shipHealth").GetProperty("emergency").GetBoolean())
throw new InvalidOperationException("Original current health guard refused production preflight.");
}
var workerStatuses=new ConcurrentQueue<WorkerStatus>();
void LogStatus(string message){workerStatuses.Enqueue(new(DateTimeOffset.UtcNow,message));Console.WriteLine(message);}
var navigation=new NativeTradeNavigation(pid,hash,()=>journal.State,NativeSupportPackage.Directory,()=>settings.NavigationFuelReserve,Check);
navigation.Status+=LogStatus;
Task ForbiddenTravel(Station station,Func<CancellationToken,Task> before,CancellationToken cancellation)
=>throw new InvalidOperationException("Acceptance does not permit a travel call.");
var production=new NativeTradeCommandDriver(channel,()=>journal.State,Path.Combine(directory,"operations"),ForbiddenTravel,Check)
{JournalDirectory=settings.JournalDirectory,RunningMenuRecoveryFactory=navigation.EnterRunningMenuRecovery};
production.Status+=LogStatus;
var barrier=new TradePauseMutationBarrier(production,production);
var runner=new TradeRunner(()=>journal.State,(market,key)=>book.Quotes.FirstOrDefault(value=>value.MarketId==market&&value.Commodity==key));
var progress=new List<RunProgress>();var progressLock=new object();
runner.Progress+=value=>{lock(progressLock)progress.Add(value);};journal.JournalEvent+=runner.Observe;journal.PlayerChanged+=runner.ObservePlayer;
var observer=new NativeTradePauseRecoveryChannel(pid,hash,Path.Combine(NativeSupportPackage.Directory,"DockingControl"));
await observer.PrepareAsync(token);
var originalReceipt=await observer.ReadAsync(token);var originalRequestId=originalReceipt.GetProperty("tradePauseRecovery").GetProperty("requestId").GetString();
using var frames=new StreamWriter(new FileStream(Path.Combine(directory,"frames.jsonl"),FileMode.CreateNew,FileAccess.Write,FileShare.Read)){AutoFlush=true};
Task? running=null;string? failure=null;JsonElement? positive=null,paused=null,resumed=null,stopped=null,finalInventory=null,finalRaw=null;
bool originalResumeConfirmed=false,pauseRevoked=false,resumeNewScope=false,stopRevoked=false,cargoUnchanged=false,walletUnchanged=false,targetUnchanged=false;
string? runningOwner=null,openRequestId=null;var pauseOpenActionsSent=0;JsonElement? pauseOpenReceipt=null;
try
{
running=runner.RunAsync(plan,ExecutionMode.NativeBridge,barrier,token);
await Task.WhenAny(running,barrier.FirstMutationHeld).WaitAsync(TimeSpan.FromSeconds(10),token);
if(runner.State!=RunState.Running||!barrier.FirstMutationHeld.IsCompleted)throw new InvalidOperationException("Production preflight did not reach a real Running mutation barrier.");
Console.WriteLine("Real TradeRunner Running; all Buy/Sell/Travel/Service calls are blocked. "+(openVerifiedPause?"One explicit verified original Pause entry is authorized.":"Waiting for an actual original Pause; no menu-opening input is sent."));
do
{
token.ThrowIfCancellationRequested();var ready=await observer.ReadAsync(token);var authorization=ready.GetProperty("tradePauseRecovery");
await frames.WriteLineAsync(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,stage="running-scope-ready",runnerState=runner.State,raw=ready}));
if(authorization.GetProperty("scopeActive").GetBoolean()&&runner.State==RunState.Running&&barrier.BlockedBuys>0&&barrier.BlockedServices>0)
{runningOwner=authorization.GetProperty("owner").GetString();break;}
if(running.IsCompleted||protection.Stopped)throw new InvalidOperationException("Real runner or health protection ended before an original menu scope became active.");
await Task.Delay(80,token);
}while(true);
if(openVerifiedPause)
{
// Live evidence: persistent075-0514j-production-01 recovery
// opened Pause on this exact game SHA. Current native dispatch
// revalidates flow/CSM and finds one actual graph state Pause.
var opener=new NativeRelogChannel(pid,hash,returnToMenuTrial:true);var openOwner=Guid.NewGuid().ToString("N");
openRequestId=Guid.NewGuid().ToString("N");
try
{
await opener.BeginAsync(openOwner,token);var frame=await opener.ReadAsync(openOwner,token);
if(runner.State!=RunState.Running||!frame.Stable||frame.State!="InGame"||DateTimeOffset.UtcNow-frame.ObservedAt>TimeSpan.FromMilliseconds(200))
throw new InvalidOperationException("One verified Pause entry requires the real Running current InGame scope.");
pauseOpenActionsSent++;await opener.SubmitAsync(openOwner,openRequestId,frame,"@pause",token);
var until=DateTimeOffset.UtcNow.AddSeconds(3);
do
{
var observed=await opener.ReadAsync(openOwner,token);
if(observed.LastRequestId==openRequestId&&observed.ActionApplied)
{pauseOpenReceipt=JsonSerializer.SerializeToElement(observed,ToolboxSettings.JsonOptions);break;}
if(observed.LastRequestId==openRequestId&&observed.ActionFailed)throw new InvalidOperationException("The original unique Pause graph action refused entry; it was not retried.");
if(DateTimeOffset.UtcNow>=until)throw new TimeoutException("Original Pause entry was not acknowledged; it was not retried.");
await Task.Delay(30,token);
}while(true);
}
finally{await opener.EndAsync(openOwner);}
}
while(true)
{
token.ThrowIfCancellationRequested();if(runner.State!=RunState.Running||protection.Stopped)throw new InvalidOperationException("The real trade runner or actual health protection stopped before pause recovery.");
var current=journal.State;
if(!current.InGame||!current.Docked||current.CommanderId!=initial.CommanderId||current.CurrentShipId!=initial.CurrentShipId
||current.LoadGameAt!=initial.LoadGameAt||current.MarketId!=initial.MarketId||!current.CargoReliable||current.CargoCount!=initial.CargoCount
||NavigationSafety.HasExternalThreat(current)||NavigationSafety.UnhealthyHull(current))throw new InvalidOperationException("The real docked session changed while observing Pause.");
var value=await observer.ReadAsync(token);await frames.WriteLineAsync(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,runnerState=runner.State,raw=value}));
var receipt=value.GetProperty("tradePauseRecovery");
if(receipt.GetProperty("applied").GetBoolean()&&receipt.GetProperty("requestId").GetString() is {Length:32}
&&receipt.GetProperty("requestId").GetString()!=originalRequestId&&receipt.GetProperty("owner").GetString()==runningOwner
&&receipt.GetProperty("scopeActive").GetBoolean()
&&value.GetProperty("state").GetString()=="InGame"&&value.GetProperty("nextState").GetString()=="InGame")
{positive=value;originalResumeConfirmed=true;break;}
await Task.Delay(80,token);
}
var owner=positive.Value.GetProperty("tradePauseRecovery").GetProperty("owner").GetString();
runner.Pause();paused=await observer.ReadAsync(token);pauseRevoked=!paused.Value.GetProperty("tradePauseRecovery").GetProperty("scopeActive").GetBoolean();
if(!pauseRevoked)throw new InvalidOperationException("User Pause did not immediately revoke the original menu scope.");
await Task.Delay(350,token);var pausedAgain=await observer.ReadAsync(token);
if(runner.State!=RunState.Paused||pausedAgain.GetProperty("tradePauseRecovery").GetProperty("scopeActive").GetBoolean())throw new InvalidOperationException("A delayed Running report reauthorized a paused trade run.");
runner.Resume();
do
{
token.ThrowIfCancellationRequested();resumed=await observer.ReadAsync(token);var scope=resumed.Value.GetProperty("tradePauseRecovery");
if(scope.GetProperty("scopeActive").GetBoolean()&&scope.GetProperty("owner").GetString()!=owner){resumeNewScope=true;break;}
await Task.Delay(80,token);
}while(true);
runner.Stop();await running.WaitAsync(TimeSpan.FromSeconds(4));stopped=await observer.ReadAsync(CancellationToken.None);
stopRevoked=runner.State==RunState.Stopped&&!stopped.Value.GetProperty("tradePauseRecovery").GetProperty("scopeActive").GetBoolean();
if(!stopRevoked)throw new InvalidOperationException("Stop did not revoke the real Running scope.");
finalInventory=await channel.CallAsync(new{op="commerce.inventory"},CancellationToken.None);finalRaw=await pipe.CallAsync(new{op="read"});
var finalLocations=await catalogue.ReadLocationsAsync();
cargoUnchanged=initialInventory.GetProperty("count").GetInt32()==finalInventory.Value.GetProperty("count").GetInt32()
&&journal.State.CargoReliable&&journal.State.CargoCount==initial.CargoCount&&journal.State.Inventory.SequenceEqual(initial.Inventory);
walletUnchanged=initialInventory.GetProperty("credits").GetInt64()==finalInventory.Value.GetProperty("credits").GetInt64();
targetUnchanged=initialLocations.Locations.Where(value=>value.Selected).Select(value=>value.Identity).Order().SequenceEqual(finalLocations.Locations.Where(value=>value.Selected).Select(value=>value.Identity).Order());
if(!cargoUnchanged||!walletUnchanged||!targetUnchanged||finalRaw.Value.GetProperty("controlApplied").GetBoolean()
||finalRaw.Value.GetProperty("shipHealth").GetProperty("emergency").GetBoolean()||finalRaw.Value.GetProperty("shipHealth").GetProperty("damageObserved").GetBoolean())
throw new InvalidOperationException("Final cargo, wallet, target, health or released control did not match.");
}
catch(Exception error){failure=error.ToString();}
finally
{
runner.Stop();if(running is not null)try{await running.WaitAsync(TimeSpan.FromSeconds(4));}catch(Exception error){failure??=error.ToString();}
journal.JournalEvent-=runner.Observe;journal.PlayerChanged-=runner.ObservePlayer;
await File.WriteAllTextAsync(Path.Combine(directory,"result.json"),JsonSerializer.Serialize(new{passed=failure is null&&originalResumeConfirmed&&pauseRevoked&&resumeNewScope&&stopRevoked&&cargoUnchanged&&walletUnchanged&&targetUnchanged,
failure,pid,gameStarted,gamePath,initialShip=initial.CurrentShipId,initial.CargoCount,initial.CargoCapacity,initialInventory,finalInventory,
originalResumeConfirmed,pauseRevoked,resumeNewScope,stopRevoked,cargoUnchanged,walletUnchanged,targetUnchanged,
positive,paused,resumed,stopped,finalRaw,progress,workerStatuses=workerStatuses.ToArray(),barrier.BlockedBuys,barrier.BlockedSells,barrier.BlockedTravels,barrier.BlockedServices,
tradeOrdersSent=TradePauseMutationBarrier.ForwardedMutations,flightInputsSent=0,stationServiceMutationsSent=0,pauseOpenActionsSent,openVerifiedPause,openRequestId,pauseOpenReceipt,
gameExited=game.HasExited,gameTerminationRequested=false,screenUse=false,osInputsSent=0,limitations="One safe docked ship and exact original Pause only. No financial, travel, service or outside-Running menu-resume claim."},ToolboxSettings.JsonOptions));
}
if(failure is not null)throw new InvalidOperationException("Bounded zero-mutation pause acceptance did not pass; see result.json.");
}
}