using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.UI.Xaml.Media;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Presentation;
using XFEEDToolBox.Core.Services;
using XFEEDToolBox.Views;
namespace XFEEDToolBox;
public partial class App
{
private static async Task<object> RunActivityLogProbeAsync(string directory)
{
Directory.CreateDirectory(directory);
string[] requirements=
[
"Use only actual Runtime activity entries; never inject a demonstration exception or replace player data.",
"Refresh CurrentSystem read-only with extension preparation disabled in --activity-only mode; never start flight, commerce or a session workflow.",
"Select LogTabs index 1 and capture the actual toolbox log page with a frozen array of real immutable entries.",
"Record severity, first/last observation, occurrence count, diagnostic text and hashes from those same entries.",
"If a real diagnostic exists, expand its actual realized row and capture it; otherwise mark expansion unconfirmed.",
"Capture success is not visual acceptance: preserve layout, visible text, screenshot SHA-256 and outstanding pixel-review requirements."
];
await File.WriteAllTextAsync(Path.Combine(directory,"activity-log-requirements.json"),JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,requirements},ToolboxSettings.JsonOptions));
var checks=new List<object>();var captures=new List<object>();var refreshes=new List<object>();var states=new List<object>();
var originalSize=MainWindow.AppWindow.Size;var beforePlayer=Runtime.Player;
var entries=Array.Empty<ActivityEntry>();var snapshotHash="";string? failure=null;long? expandedEntryId=null;
LogViewPage? page=null;Pivot? tabs=null;ListView? list=null;var previousTab=0;var failed=0;var unconfirmed=0;
void Check(string id,bool? passed,string evidence)
{
if(passed==false)failed++;if(passed is null)unconfirmed++;
checks.Add(new{id,status=passed is null?"unconfirmed":passed.Value?"passed":"failed",evidence});
}
static string Hash(string value)=>Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(value)));
static string EntriesHash(ActivityEntry[] value)=>Convert.ToHexStringLower(SHA256.HashData(JsonSerializer.SerializeToUtf8Bytes(value,ToolboxSettings.JsonOptions)));
bool Visible(FrameworkElement element)
{
DependencyObject? current=element;
while(current is not null)
{
if(current is UIElement ui&&ui.Visibility!=Visibility.Visible)return false;
if(ReferenceEquals(current,page))break;
current=VisualTreeHelper.GetParent(current);
}
return element.ActualWidth>0&&element.ActualHeight>0;
}
object? Bounds(FrameworkElement? element)
{
if(page is null||element is null||element.ActualWidth<=0||element.ActualHeight<=0)return null;
var origin=element.TransformToVisual(page).TransformPoint(new(0,0));
return new{element.Name,x=origin.X,y=origin.Y,width=element.ActualWidth,height=element.ActualHeight,
right=origin.X+element.ActualWidth,bottom=origin.Y+element.ActualHeight,visible=Visible(element)};
}
async Task Capture(string name)
{
// Reapply the frozen real snapshot, rather than reevaluating Runtime
// enumerables at different points while capture awaits its pixels.
ViewModel.Activity.Apply(entries);page!.UpdateLayout();await Task.Delay(100);
var rowsBefore=ViewModel.Activity.Select(row=>row.Record).ToArray();
var path=Path.Combine(directory,name+".png");await CapturePageAsync(page,path);
var imageHash=Convert.ToHexStringLower(SHA256.HashData(await File.ReadAllBytesAsync(path)));
var rowsAfter=ViewModel.Activity.Select(row=>row.Record).ToArray();
var stable=rowsBefore.SequenceEqual(entries)&&rowsAfter.SequenceEqual(entries);
var texts=Descendants(page).OfType<TextBlock>().Where(Visible).Select(text=>text.Text).Where(text=>!string.IsNullOrWhiteSpace(text)).Distinct().ToArray();
var horizontal=Descendants(list!).OfType<ScrollViewer>().Any(scroll=>Visible(scroll)&&scroll.ScrollableWidth>1);
captures.Add(new{name,path,sha256=imageHash,bytes=new FileInfo(path).Length});
states.Add(new{name,pageWidth=page.ActualWidth,pageHeight=page.ActualHeight,windowWidth=MainWindow.AppWindow.Size.Width,windowHeight=MainWindow.AppWindow.Size.Height,
selectedTab=tabs!.SelectedIndex,list=Bounds(list),horizontalOverflow=horizontal,snapshotHash,renderedSnapshotHash=EntriesHash(rowsAfter),snapshotStable=stable,
realizedRowCount=Descendants(list!).OfType<ListViewItem>().Count(Visible),expandedEntryId,
expandedDetails=Descendants(list!).OfType<Expander>().Where(expander=>Visible(expander)&&expander.IsExpanded).Select(expander=>new{header=expander.Header?.ToString(),bounds=Bounds(expander)}).ToArray(),texts});
Check("frozen-source-"+name,stable,"Visible activity records before and after pixel capture must equal the frozen actual Runtime entries, including their counts and diagnostics.");
Check("layout-"+name,!horizontal&&page.ActualWidth>0&&page.ActualHeight>0,"The selected toolbox log has a positive actual viewport and no realized horizontal scrolling; original pixels still require review.");
}
try
{
var args=Environment.GetCommandLineArgs();
if(!args.Contains("--activity-only"))throw new InvalidOperationException("Activity acceptance requires --activity-only so CurrentSystem extension preparation is disabled.");
if(ViewModel.DemoMode||ViewModel.OfflinePreview)throw new InvalidOperationException("Activity acceptance requires real Runtime data, without demonstration or offline substitution.");
if(ViewModel.RunActive||ViewModel.NavigationActive||ViewModel.SystemFlightActive||ViewModel.SessionBusy)
throw new InvalidOperationException("Activity acceptance cannot share this application with an active trade, flight or session operation.");
ViewModel.ShowCurrentSystem();
try
{
for(var attempt=0;attempt<3;attempt++)
{
var elapsed=Stopwatch.StartNew();await ViewModel.RefreshCurrentSystemCommand.ExecuteAsync(null);
var until=DateTimeOffset.UtcNow.AddSeconds(20);
while(ViewModel.CurrentSystemReading&&DateTimeOffset.UtcNow<until)await Task.Delay(50);
refreshes.Add(new{attempt=attempt+1,at=DateTimeOffset.UtcNow,elapsedMilliseconds=elapsed.Elapsed.TotalMilliseconds,
ViewModel.CurrentSystemReading,ViewModel.CurrentSystemStatus,ViewModel.SystemControlAvailability,
source=ViewModel.CurrentSystemSceneSnapshot is {} source?new{source.SystemAddress,source.SystemName,source.EntityId,source.Sequence,source.ObservedAt}:null});
if(ViewModel.CurrentSystemReading)throw new TimeoutException("The actual read-only current-system refresh did not finish within the observation window.");
await Task.Delay(120);
}
}
finally{ViewModel.HideCurrentSystem();}
entries=Runtime.Activity.ToArray();snapshotHash=EntriesHash(entries);
Check("real-current-system-observation",entries.Any(entry=>entry.Code.StartsWith("current-system.",StringComparison.Ordinal)),"The source is actual read-only refresh output, whether normal loading, successful reading or a real diagnostic. No expected outcome was manufactured.");
Check("bounded-activity-snapshot",entries.Length<=200,"The actual Runtime retains the latest bounded activity snapshot; ordinary execution events remain separate.");
Check("diagnostic-summary-separation",entries.Where(entry=>entry.HasDiagnostic).All(entry=>!entry.Message.Contains("\n at ",StringComparison.Ordinal)&&!entry.Message.Contains("\n 在 ",StringComparison.Ordinal)),"Full stacks belong to diagnostic details rather than the visible summary.");
var repeated=entries.Where(entry=>entry.Occurrences>1).ToArray();
Check("real-aggregation",repeated.Length>0?repeated.All(entry=>entry.LastSeen>=entry.Time&&entry.RepetitionDisplay.Length>0):null,
repeated.Length>0?"Real repeated observations retain first/last time and a visible count.":"No actual repeated diagnostic/status was observed; none was inserted for the probe.");
AppShellPage.Current!.Navigate(typeof(LogViewPage));await Task.Delay(200);page=(LogViewPage)AppShellPage.Current.CurrentPage!;
tabs=(Pivot)page.FindName("LogTabs");previousTab=tabs.SelectedIndex;tabs.SelectedIndex=1;
ViewModel.Activity.Apply(entries);page.UpdateLayout();await Task.Delay(160);
list=Descendants(page).OfType<ListView>().FirstOrDefault(candidate=>ReferenceEquals(candidate.ItemsSource,ViewModel.Activity))
??throw new InvalidOperationException("The actual toolbox activity ListView was not realized.");
foreach(var expander in Descendants(list).OfType<Expander>())expander.IsExpanded=false;
Check("toolbox-tab-selected",tabs.SelectedIndex==1,"LogTabs index 1 is the toolbox activity tab.");
await Capture("activity-log-summary");
var diagnostic=entries.FirstOrDefault(entry=>entry.HasDiagnostic);
if(diagnostic is null)
Check("real-diagnostic-expanded",null,"No real Runtime entry contains diagnostic details; expansion remains unconfirmed and no fake error was generated.");
else
{
var row=ViewModel.Activity.Single(value=>value.Record.Id==diagnostic.Id);list.ScrollIntoView(row);page.UpdateLayout();await Task.Delay(160);
var container=list.ContainerFromItem(row) as FrameworkElement;
var expander=container is null?null:Descendants(container).OfType<Expander>().FirstOrDefault(value=>value.Header?.ToString()=="诊断详情");
if(expander is null)Check("real-diagnostic-expanded",null,"A real diagnostic exists but its actual Expander was not realized; no substitute row was created.");
else
{
expandedEntryId=diagnostic.Id;expander.IsExpanded=true;page.UpdateLayout();expander.StartBringIntoView(new BringIntoViewOptions{AnimationDesired=false});await Task.Delay(120);
Check("real-diagnostic-expanded",expander.IsExpanded&&Visible(expander),"Expanded the existing real diagnostic row identified by its stable ActivityEntry Id.");
await Capture("activity-log-real-diagnostic");expander.IsExpanded=false;expandedEntryId=null;
}
}
Check("no-execution-started",!ViewModel.RunActive&&!ViewModel.NavigationActive&&!ViewModel.SystemFlightActive&&!ViewModel.SessionBusy,
"This probe invoked only read-only CurrentSystem refresh and log-page presentation; no execution, module preparation, menu operation or OS input was requested.");
}
catch(Exception error){failure=error.ToString();}
finally
{
ViewModel.HideCurrentSystem();if(tabs is not null)tabs.SelectedIndex=previousTab;MainWindow.AppWindow.Resize(originalSize);
var frozenChecks=checks.ToArray();var frozenCaptures=captures.ToArray();var frozenStates=states.ToArray();var frozenRefreshes=refreshes.ToArray();
var frozenEntries=entries.Select(entry=>new{entry.Id,entry.Time,entry.LastSeen,entry.ClosedAt,entry.Kind,entry.Code,entry.Message,entry.FirstMessage,
severity=entry.Severity.ToString(),entry.SeverityDisplay,entry.Occurrences,entry.RepetitionDisplay,entry.Resolved,entry.Superseded,entry.HasDiagnostic,
entry.FirstDiagnostic,entry.LastDiagnostic,details=entry.DiagnosticDisplay,detailsSha256=Hash(entry.DiagnosticDisplay)}).ToArray();
var current=Runtime.Player;
await File.WriteAllTextAsync(Path.Combine(directory,"activity-log-evidence.json"),JsonSerializer.Serialize(new
{
at=DateTimeOffset.UtcNow,requirements,checks=frozenChecks,captures=frozenCaptures,states=frozenStates,refreshes=frozenRefreshes,entries=frozenEntries,snapshotHash,
actualDataDirectory=Runtime.DataDirectory,failure,failedChecks=failed,unconfirmedChecks=unconfirmed,structurePassed=failure is null&&failed==0,
playerObservation=new{before=new{beforePlayer.CommanderId,beforePlayer.CurrentShipId,beforePlayer.LoadGameAt,beforePlayer.CargoCount,beforePlayer.Credits},
after=new{current.CommanderId,current.CurrentShipId,current.LoadGameAt,current.CargoCount,current.Credits}},
source="actual-runtime-activity-after-read-only-current-system-refresh",syntheticExceptions=false,syntheticPlayerData=false,
gameFlightCommandsInvoked=false,tradeOrdersSubmitted=0,sessionOperationsInvoked=false,modulePreparationInvoked=false,gameRestartInvoked=false,osInputsSent=0,
visualReview="Original pixels, local details and OCR still require independent visual inspection. Missing real diagnostics leave expansion unconfirmed."
},ToolboxSettings.JsonOptions));
}
return new{failure,checks=checks.ToArray(),captures=captures.ToArray(),failedChecks=failed,unconfirmedChecks=unconfirmed,structurePassed=failure is null&&failed==0,
entryCount=entries.Length,snapshotHash,realDiagnosticCount=entries.Count(entry=>entry.HasDiagnostic),gameActionsInvoked=false,syntheticExceptions=false};
}
}
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.UI.Xaml.Media;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Presentation;
using XFEEDToolBox.Core.Services;
using XFEEDToolBox.Views;
namespace XFEEDToolBox;
public partial class App
{
private static async Task<object> RunActivityLogProbeAsync(string directory)
{
Directory.CreateDirectory(directory);
string[] requirements=
[
"Use only actual Runtime activity entries; never inject a demonstration exception or replace player data.",
"Refresh CurrentSystem read-only with extension preparation disabled in --activity-only mode; never start flight, commerce or a session workflow.",
"Select LogTabs index 1 and capture the actual toolbox log page with a frozen array of real immutable entries.",
"Record severity, first/last observation, occurrence count, diagnostic text and hashes from those same entries.",
"If a real diagnostic exists, expand its actual realized row and capture it; otherwise mark expansion unconfirmed.",
"Capture success is not visual acceptance: preserve layout, visible text, screenshot SHA-256 and outstanding pixel-review requirements."
];
await File.WriteAllTextAsync(Path.Combine(directory,"activity-log-requirements.json"),JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,requirements},ToolboxSettings.JsonOptions));
var checks=new List<object>();var captures=new List<object>();var refreshes=new List<object>();var states=new List<object>();
var originalSize=MainWindow.AppWindow.Size;var beforePlayer=Runtime.Player;
var entries=Array.Empty<ActivityEntry>();var snapshotHash="";string? failure=null;long? expandedEntryId=null;
LogViewPage? page=null;Pivot? tabs=null;ListView? list=null;var previousTab=0;var failed=0;var unconfirmed=0;
void Check(string id,bool? passed,string evidence)
{
if(passed==false)failed++;if(passed is null)unconfirmed++;
checks.Add(new{id,status=passed is null?"unconfirmed":passed.Value?"passed":"failed",evidence});
}
static string Hash(string value)=>Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(value)));
static string EntriesHash(ActivityEntry[] value)=>Convert.ToHexStringLower(SHA256.HashData(JsonSerializer.SerializeToUtf8Bytes(value,ToolboxSettings.JsonOptions)));
bool Visible(FrameworkElement element)
{
DependencyObject? current=element;
while(current is not null)
{
if(current is UIElement ui&&ui.Visibility!=Visibility.Visible)return false;
if(ReferenceEquals(current,page))break;
current=VisualTreeHelper.GetParent(current);
}
return element.ActualWidth>0&&element.ActualHeight>0;
}
object? Bounds(FrameworkElement? element)
{
if(page is null||element is null||element.ActualWidth<=0||element.ActualHeight<=0)return null;
var origin=element.TransformToVisual(page).TransformPoint(new(0,0));
return new{element.Name,x=origin.X,y=origin.Y,width=element.ActualWidth,height=element.ActualHeight,
right=origin.X+element.ActualWidth,bottom=origin.Y+element.ActualHeight,visible=Visible(element)};
}
async Task Capture(string name)
{
// Reapply the frozen real snapshot, rather than reevaluating Runtime
// enumerables at different points while capture awaits its pixels.
ViewModel.Activity.Apply(entries);page!.UpdateLayout();await Task.Delay(100);
var rowsBefore=ViewModel.Activity.Select(row=>row.Record).ToArray();
var path=Path.Combine(directory,name+".png");await CapturePageAsync(page,path);
var imageHash=Convert.ToHexStringLower(SHA256.HashData(await File.ReadAllBytesAsync(path)));
var rowsAfter=ViewModel.Activity.Select(row=>row.Record).ToArray();
var stable=rowsBefore.SequenceEqual(entries)&&rowsAfter.SequenceEqual(entries);
var texts=Descendants(page).OfType<TextBlock>().Where(Visible).Select(text=>text.Text).Where(text=>!string.IsNullOrWhiteSpace(text)).Distinct().ToArray();
var horizontal=Descendants(list!).OfType<ScrollViewer>().Any(scroll=>Visible(scroll)&&scroll.ScrollableWidth>1);
captures.Add(new{name,path,sha256=imageHash,bytes=new FileInfo(path).Length});
states.Add(new{name,pageWidth=page.ActualWidth,pageHeight=page.ActualHeight,windowWidth=MainWindow.AppWindow.Size.Width,windowHeight=MainWindow.AppWindow.Size.Height,
selectedTab=tabs!.SelectedIndex,list=Bounds(list),horizontalOverflow=horizontal,snapshotHash,renderedSnapshotHash=EntriesHash(rowsAfter),snapshotStable=stable,
realizedRowCount=Descendants(list!).OfType<ListViewItem>().Count(Visible),expandedEntryId,
expandedDetails=Descendants(list!).OfType<Expander>().Where(expander=>Visible(expander)&&expander.IsExpanded).Select(expander=>new{header=expander.Header?.ToString(),bounds=Bounds(expander)}).ToArray(),texts});
Check("frozen-source-"+name,stable,"Visible activity records before and after pixel capture must equal the frozen actual Runtime entries, including their counts and diagnostics.");
Check("layout-"+name,!horizontal&&page.ActualWidth>0&&page.ActualHeight>0,"The selected toolbox log has a positive actual viewport and no realized horizontal scrolling; original pixels still require review.");
}
try
{
var args=Environment.GetCommandLineArgs();
if(!args.Contains("--activity-only"))throw new InvalidOperationException("Activity acceptance requires --activity-only so CurrentSystem extension preparation is disabled.");
if(ViewModel.DemoMode||ViewModel.OfflinePreview)throw new InvalidOperationException("Activity acceptance requires real Runtime data, without demonstration or offline substitution.");
if(ViewModel.RunActive||ViewModel.NavigationActive||ViewModel.SystemFlightActive||ViewModel.SessionBusy)
throw new InvalidOperationException("Activity acceptance cannot share this application with an active trade, flight or session operation.");
ViewModel.ShowCurrentSystem();
try
{
for(var attempt=0;attempt<3;attempt++)
{
var elapsed=Stopwatch.StartNew();await ViewModel.RefreshCurrentSystemCommand.ExecuteAsync(null);
var until=DateTimeOffset.UtcNow.AddSeconds(20);
while(ViewModel.CurrentSystemReading&&DateTimeOffset.UtcNow<until)await Task.Delay(50);
refreshes.Add(new{attempt=attempt+1,at=DateTimeOffset.UtcNow,elapsedMilliseconds=elapsed.Elapsed.TotalMilliseconds,
ViewModel.CurrentSystemReading,ViewModel.CurrentSystemStatus,ViewModel.SystemControlAvailability,
source=ViewModel.CurrentSystemSceneSnapshot is {} source?new{source.SystemAddress,source.SystemName,source.EntityId,source.Sequence,source.ObservedAt}:null});
if(ViewModel.CurrentSystemReading)throw new TimeoutException("The actual read-only current-system refresh did not finish within the observation window.");
await Task.Delay(120);
}
}
finally{ViewModel.HideCurrentSystem();}
entries=Runtime.Activity.ToArray();snapshotHash=EntriesHash(entries);
Check("real-current-system-observation",entries.Any(entry=>entry.Code.StartsWith("current-system.",StringComparison.Ordinal)),"The source is actual read-only refresh output, whether normal loading, successful reading or a real diagnostic. No expected outcome was manufactured.");
Check("bounded-activity-snapshot",entries.Length<=200,"The actual Runtime retains the latest bounded activity snapshot; ordinary execution events remain separate.");
Check("diagnostic-summary-separation",entries.Where(entry=>entry.HasDiagnostic).All(entry=>!entry.Message.Contains("\n at ",StringComparison.Ordinal)&&!entry.Message.Contains("\n 在 ",StringComparison.Ordinal)),"Full stacks belong to diagnostic details rather than the visible summary.");
var repeated=entries.Where(entry=>entry.Occurrences>1).ToArray();
Check("real-aggregation",repeated.Length>0?repeated.All(entry=>entry.LastSeen>=entry.Time&&entry.RepetitionDisplay.Length>0):null,
repeated.Length>0?"Real repeated observations retain first/last time and a visible count.":"No actual repeated diagnostic/status was observed; none was inserted for the probe.");
AppShellPage.Current!.Navigate(typeof(LogViewPage));await Task.Delay(200);page=(LogViewPage)AppShellPage.Current.CurrentPage!;
tabs=(Pivot)page.FindName("LogTabs");previousTab=tabs.SelectedIndex;tabs.SelectedIndex=1;
ViewModel.Activity.Apply(entries);page.UpdateLayout();await Task.Delay(160);
list=Descendants(page).OfType<ListView>().FirstOrDefault(candidate=>ReferenceEquals(candidate.ItemsSource,ViewModel.Activity))
??throw new InvalidOperationException("The actual toolbox activity ListView was not realized.");
foreach(var expander in Descendants(list).OfType<Expander>())expander.IsExpanded=false;
Check("toolbox-tab-selected",tabs.SelectedIndex==1,"LogTabs index 1 is the toolbox activity tab.");
await Capture("activity-log-summary");
var diagnostic=entries.FirstOrDefault(entry=>entry.HasDiagnostic);
if(diagnostic is null)
Check("real-diagnostic-expanded",null,"No real Runtime entry contains diagnostic details; expansion remains unconfirmed and no fake error was generated.");
else
{
var row=ViewModel.Activity.Single(value=>value.Record.Id==diagnostic.Id);list.ScrollIntoView(row);page.UpdateLayout();await Task.Delay(160);
var container=list.ContainerFromItem(row) as FrameworkElement;
var expander=container is null?null:Descendants(container).OfType<Expander>().FirstOrDefault(value=>value.Header?.ToString()=="诊断详情");
if(expander is null)Check("real-diagnostic-expanded",null,"A real diagnostic exists but its actual Expander was not realized; no substitute row was created.");
else
{
expandedEntryId=diagnostic.Id;expander.IsExpanded=true;page.UpdateLayout();expander.StartBringIntoView(new BringIntoViewOptions{AnimationDesired=false});await Task.Delay(120);
Check("real-diagnostic-expanded",expander.IsExpanded&&Visible(expander),"Expanded the existing real diagnostic row identified by its stable ActivityEntry Id.");
await Capture("activity-log-real-diagnostic");expander.IsExpanded=false;expandedEntryId=null;
}
}
Check("no-execution-started",!ViewModel.RunActive&&!ViewModel.NavigationActive&&!ViewModel.SystemFlightActive&&!ViewModel.SessionBusy,
"This probe invoked only read-only CurrentSystem refresh and log-page presentation; no execution, module preparation, menu operation or OS input was requested.");
}
catch(Exception error){failure=error.ToString();}
finally
{
ViewModel.HideCurrentSystem();if(tabs is not null)tabs.SelectedIndex=previousTab;MainWindow.AppWindow.Resize(originalSize);
var frozenChecks=checks.ToArray();var frozenCaptures=captures.ToArray();var frozenStates=states.ToArray();var frozenRefreshes=refreshes.ToArray();
var frozenEntries=entries.Select(entry=>new{entry.Id,entry.Time,entry.LastSeen,entry.ClosedAt,entry.Kind,entry.Code,entry.Message,entry.FirstMessage,
severity=entry.Severity.ToString(),entry.SeverityDisplay,entry.Occurrences,entry.RepetitionDisplay,entry.Resolved,entry.Superseded,entry.HasDiagnostic,
entry.FirstDiagnostic,entry.LastDiagnostic,details=entry.DiagnosticDisplay,detailsSha256=Hash(entry.DiagnosticDisplay)}).ToArray();
var current=Runtime.Player;
await File.WriteAllTextAsync(Path.Combine(directory,"activity-log-evidence.json"),JsonSerializer.Serialize(new
{
at=DateTimeOffset.UtcNow,requirements,checks=frozenChecks,captures=frozenCaptures,states=frozenStates,refreshes=frozenRefreshes,entries=frozenEntries,snapshotHash,
actualDataDirectory=Runtime.DataDirectory,failure,failedChecks=failed,unconfirmedChecks=unconfirmed,structurePassed=failure is null&&failed==0,
playerObservation=new{before=new{beforePlayer.CommanderId,beforePlayer.CurrentShipId,beforePlayer.LoadGameAt,beforePlayer.CargoCount,beforePlayer.Credits},
after=new{current.CommanderId,current.CurrentShipId,current.LoadGameAt,current.CargoCount,current.Credits}},
source="actual-runtime-activity-after-read-only-current-system-refresh",syntheticExceptions=false,syntheticPlayerData=false,
gameFlightCommandsInvoked=false,tradeOrdersSubmitted=0,sessionOperationsInvoked=false,modulePreparationInvoked=false,gameRestartInvoked=false,osInputsSent=0,
visualReview="Original pixels, local details and OCR still require independent visual inspection. Missing real diagnostics leave expansion unconfirmed."
},ToolboxSettings.JsonOptions));
}
return new{failure,checks=checks.ToArray(),captures=captures.ToArray(),failedChecks=failed,unconfirmedChecks=unconfirmed,structurePassed=failure is null&&failed==0,
entryCount=entries.Length,snapshotHash,realDiagnosticCount=entries.Count(entry=>entry.HasDiagnostic),gameActionsInvoked=false,syntheticExceptions=false};
}
}