SboxSuperuser/Engine/Storage/HostAdminStore.cs

Storage backend for host-side admin data. It maps logical StoreKey to engine storage directories, delegates reads/writes/backups/pruning to a StoreCoordinator, translates status enums, reports degraded states to log, and raises StorageChanged events for certain write outcomes.

File Access
using Sandbox;
using SboxSuperuser.Core.Storage;

namespace SboxSuperuser.Engine.Storage;

internal sealed class HostAdminStore : IAdminStorageBackend, Audit.IAuditRetention
{
	private readonly StoreCoordinator _coordinator;
	private readonly string _root;
	internal event Action<StoreKey> StorageChanged;

	internal HostAdminStore( bool dedicated, Func<bool> available )
	{
		_root = dedicated ? "superuser/store/dedicated" : "superuser/store/editor";
		bool IsAvailable() => ThreadSafe.IsMainThread && available();
		_coordinator = new StoreCoordinator( new EngineStoreFiles( IsAvailable ), IsAvailable );
	}

	public async Task<StoreReadResult> ReadAsync( StoreKey key, int supportedSchemaVersion, CancellationToken cancellation )
	{
		var result = await _coordinator.ReadAsync( DirectoryFor( key ), supportedSchemaVersion, cancellation );
		Report( key, result.Status );
		return new StoreReadResult( ReadStatus( result.Status ), result.Document is null ? null
			: new VersionedStoreDocument( result.Document.SchemaVersion, result.Document.Revision, result.Document.PayloadJson ) );
	}

	public Task<StoragePruneResult> PruneAuditAsync( long expectedRevision, CancellationToken cancellation )
	{
		// No caller-controlled key: general policy and owner backups never enter automatic retention.
		return _coordinator.PruneAuditHistoryAsync( DirectoryFor( new StoreKey( "audit", "journal" ) ), expectedRevision, cancellation );
	}

	public async Task<StoreRetentionResult> PruneAuditHistoryAsync( long expectedRevision, CancellationToken cancellation )
	{
		var result = await PruneAuditAsync( expectedRevision, cancellation );
		return new StoreRetentionResult( Enum.Parse<StoreWriteStatus>( result.Status.ToString() ), result.HasMore );
	}

	public async Task<StoreWriteResult> WriteAsync( StoreWriteRequest request, CancellationToken cancellation )
	{
		ArgumentNullException.ThrowIfNull( request );
		var result = await _coordinator.WriteAsync( DirectoryFor( request.Key ), request.Document.SchemaVersion,
			request.ExpectedRevision, request.Document.PayloadJson, cancellation );
		Report( request.Key, result.Status );
		NotifyChange( request.Key, result.Status );
		return WriteResult( result );
	}

	public async Task<StoreBackupList> GetBackupsAsync( StoreKey key, CancellationToken cancellation )
	{
		var result = await _coordinator.GetBackupsAsync( DirectoryFor( key ), cancellation );
		Report( key, result.Status );
		return new StoreBackupList( ReadStatus( result.Status ), result.HeadRevision,
			result.Documents.Select( document => new StoreBackup( document.GenerationId, document.Revision, document.SchemaVersion ) ), result.HasMore );
	}

	public async Task<StoreWriteResult> RecoverAsync( StoreKey key, Guid generationId, long expectedHeadRevision,
		AuditIntent recoveryIntent, CancellationToken cancellation )
	{
		ArgumentNullException.ThrowIfNull( recoveryIntent );
		if ( generationId == Guid.Empty || expectedHeadRevision < 1 )
		{
			throw new ArgumentException( "Recovery requires an explicit generation and positive observed head revision." );
		}

		var result = await _coordinator.RecoverAsync( DirectoryFor( key ), generationId, expectedHeadRevision,
			recoveryIntent.ToJson(), cancellation );
		Report( key, result.Status );
		NotifyChange( key, result.Status );
		return WriteResult( result );
	}

	private void NotifyChange( StoreKey key, StorageStatus status )
	{
		if ( status is StorageStatus.Committed or StorageStatus.Unknown or StorageStatus.Corrupt or StorageStatus.Conflict )
		{
			StorageChanged?.Invoke( key );
		}
	}

	private string DirectoryFor( StoreKey key )
	{
		ArgumentNullException.ThrowIfNull( key );
		return $"{_root}/{key.ModuleId}/{key.DocumentId}";
	}

	private static StoreReadStatus ReadStatus( StorageStatus status ) => status switch
	{
		StorageStatus.Found => StoreReadStatus.Found,
		StorageStatus.Missing => StoreReadStatus.Missing,
		StorageStatus.Corrupt => StoreReadStatus.Corrupt,
		StorageStatus.UnsupportedSchema => StoreReadStatus.UnsupportedSchema,
		StorageStatus.Unavailable => StoreReadStatus.Unavailable,
		StorageStatus.Cancelled => StoreReadStatus.Cancelled,
		_ => StoreReadStatus.Unknown
	};

	private static StoreWriteResult WriteResult( StorageOutcome outcome )
	{
		var status = outcome.Status switch
		{
			StorageStatus.Committed => StoreWriteStatus.Committed,
			StorageStatus.Conflict => StoreWriteStatus.Conflict,
			StorageStatus.UnsupportedSchema => StoreWriteStatus.UnsupportedSchema,
			StorageStatus.Unavailable => StoreWriteStatus.Unavailable,
			StorageStatus.Cancelled => StoreWriteStatus.Cancelled,
			StorageStatus.InvalidData => StoreWriteStatus.InvalidData,
			StorageStatus.Corrupt => StoreWriteStatus.Corrupt,
			_ => StoreWriteStatus.Unknown
		};
		return new StoreWriteResult( status, status == StoreWriteStatus.Committed ? outcome.Document.Revision : null );
	}

	private static void Report( StoreKey key, StorageStatus status )
	{
		if ( status is StorageStatus.Corrupt or StorageStatus.UnsupportedSchema or StorageStatus.Unavailable or StorageStatus.Unknown )
		{
			Log.Warning( $"[Superuser] Storage degraded; key={key.ModuleId}/{key.DocumentId}; status={status}. Existing files preserved." );
		}
	}
}