diff --git a/app/TrimSmokeTest/JsonContractWalker.cs b/app/TrimSmokeTest/JsonContractWalker.cs
index 73af51fb2..392087164 100644
--- a/app/TrimSmokeTest/JsonContractWalker.cs
+++ b/app/TrimSmokeTest/JsonContractWalker.cs
@@ -45,7 +45,10 @@ public static class JsonContractWalker
return;
}
- foreach (var property in typeInfo.Properties)
+ //
+ // System.Text.Json never meets the type of an ignored property, e.g., one marked with JsonIgnore:
+ //
+ foreach (var property in typeInfo.Properties.Where(property => property is not { Get: null, Set: null, AssociatedParameter: null }))
Visit(property.PropertyType, $"{path} → {property.Name}", options, visited, failures);
if (typeInfo.KeyType is { } keyType)
diff --git a/app/TrimSmokeTest/JsonRoots.cs b/app/TrimSmokeTest/JsonRoots.cs
index 909f3d79b..578232cb3 100644
--- a/app/TrimSmokeTest/JsonRoots.cs
+++ b/app/TrimSmokeTest/JsonRoots.cs
@@ -1,65 +1,38 @@
using System.Text.Json;
-
-using AIStudio.Agents;
-using AIStudio.Agents.AssistantAudit;
-using AIStudio.Assistants.Builder;
-using AIStudio.Assistants.PromptOptimizer;
-using AIStudio.Assistants.SWOTAnalysis;
-using AIStudio.Assistants.VisualBriefing;
-using AIStudio.Chat;
-using AIStudio.Settings;
-using AIStudio.Settings.DataModel;
-using AIStudio.Settings.DataModel.PreviousModels;
-using AIStudio.Tools;
-using AIStudio.Tools.DataBackup;
+using System.Text.Json.Serialization;
namespace AIStudio.TrimSmokeTest;
///
-/// The types which AI Studio reads or writes as JSON on their own, not only as part of another type.
+/// The types which AI Studio reads or writes as JSON, each with the options it uses for them.
///
///
-/// Whatever these types hold, i.e., the types of their properties, the elements of their collections, and
-/// their derived types, gets checked along with them, so only the outermost types belong here. Add a type
-/// whenever AI Studio starts to store a new kind of file or to read a new kind of model answer.
-///
-/// The answers of models are read with options of their own, which are private and only change names and
-/// leniency. The default options build the same contracts for the types of .NET they hold. A type which is
-/// private to its parser, such as the DTO of a slide deck, is out of reach here.
+/// The options of AI Studio each name the context which knows their types, so the types registered in that
+/// context are exactly those which System.Text.Json gets to read or write with these options. Whatever these
+/// types hold, i.e., the types of their properties, the elements of their collections, and their derived
+/// types, gets checked along with them. A new type, or new options, need nothing here: they are found
+/// through their context.
///
public static class JsonRoots
{
- public static readonly IReadOnlyList ALL =
- [
- // Chats, in the workspaces, among the temporary chats, and in chat archives:
- new(typeof(ChatThread), WorkspaceBehaviour.JSON_OPTIONS),
+ ///
+ /// Returns every type registered in a context of the options of AI Studio, together with these options.
+ ///
+ ///
+ /// Options which several members share are checked once. Like AppJsonOptions.FindAll, this needs the
+ /// static state of the app to be filled before.
+ ///
+ public static IReadOnlyList FindAll() => AppJsonOptions.FindAll().Values
+ .Distinct(ReferenceEqualityComparer.Instance)
+ .Cast()
+ .SelectMany(options => options.TypeInfoResolverChain
+ .OfType()
+ .SelectMany(RegisteredTypes)
+ .Select(type => new JsonRoot(type, options)))
+ .ToList();
- // The settings, and their older versions, which the migrations still read:
- new(typeof(Data), SettingsManager.JSON_OPTIONS),
- new(typeof(DataV1V3), SettingsManager.JSON_OPTIONS),
- new(typeof(DataV4), SettingsManager.JSON_OPTIONS),
- new(typeof(DataV5), SettingsManager.JSON_OPTIONS),
-
- // The projects of the Visual Briefing assistant:
- new(typeof(VisualBriefingManifest), VisualBriefingJson.Persistence),
- new(typeof(VisualBriefingBuildRecord), VisualBriefingJson.Persistence),
- new(typeof(VisualBriefingEvidenceArtifact), VisualBriefingJson.Persistence),
- new(typeof(VisualBriefingPlanArtifact), VisualBriefingJson.Persistence),
- new(typeof(VisualBriefingContentArtifact), VisualBriefingJson.Persistence),
- new(typeof(VisualBriefingPresentationArtifact), VisualBriefingJson.Persistence),
-
- // Backups and their restore:
- new(typeof(DataBackupManifest), DataBackupFormat.JSON_OPTIONS),
- new(typeof(DataRestorePlan), DataBackupFormat.JSON_OPTIONS),
- new(typeof(DataRestoreReport), DataBackupFormat.JSON_OPTIONS),
-
- // Answers of models:
- new(typeof(LuaResponse), JsonSerializerOptions.Default),
- new(typeof(LauncherTextsResponse), JsonSerializerOptions.Default),
- new(typeof(PromptOptimizationResult), JsonSerializerOptions.Default),
- new(typeof(RetrievalContextValidationResult), JsonSerializerOptions.Default),
- new(typeof(AssistantAuditResult), JsonSerializerOptions.Default),
- new(typeof(SwotCategoryAnalysisResult), JsonSerializerOptions.Default),
- new(typeof(SwotModerationResult), JsonSerializerOptions.Default),
- ];
+ private static IEnumerable RegisteredTypes(JsonSerializerContext context) => context.GetType()
+ .GetCustomAttributesData()
+ .Where(attribute => attribute.AttributeType == typeof(JsonSerializableAttribute))
+ .Select(attribute => (Type)attribute.ConstructorArguments[0].Value!);
}
\ No newline at end of file
diff --git a/app/TrimSmokeTest/Program.cs b/app/TrimSmokeTest/Program.cs
index 3309fc329..44a531924 100644
--- a/app/TrimSmokeTest/Program.cs
+++ b/app/TrimSmokeTest/Program.cs
@@ -15,14 +15,15 @@ public static class Program
//
AIStudio.Program.LOGGER_FACTORY = NullLoggerFactory.Instance;
+ var roots = JsonRoots.FindAll();
var failures = new List();
- foreach (var root in JsonRoots.ALL)
+ foreach (var root in roots)
failures.AddRange(JsonContractWalker.FindBrokenContracts(root));
failures.AddRange(JsonRoundTrips.FindLosses());
if (failures.Count is 0)
{
- Console.WriteLine($"- All {JsonRoots.ALL.Count} JSON roots can be read and written.");
+ Console.WriteLine($"- All {roots.Count} JSON roots can be read and written.");
return 0;
}
diff --git a/app/TrimSmokeTest/TrimSmokeTest.csproj b/app/TrimSmokeTest/TrimSmokeTest.csproj
index 02b77017b..5a39621af 100644
--- a/app/TrimSmokeTest/TrimSmokeTest.csproj
+++ b/app/TrimSmokeTest/TrimSmokeTest.csproj
@@ -21,6 +21,16 @@
+
+
+
+
+