feat(tooling)!: STJ-4 + STJ-5 + #116 closure — Fallout.Tooling engine + Generator on System.Text.Json#172
Merged
Conversation
…118, closes #116) The Fallout.Tooling engine and Fallout.Tooling.Generator are now STJ-native. Closes the residual #116 tail (Object.ToJObject + Newtonsoft.Json PackageRef in Fallout.Utilities.Text.Json). Fallout.Common still references Newtonsoft for the *Tasks.cs files until #119 (STJ-6) lands. Engine (#117): - Options.InternalOptions: JObject → JsonObject. - Options.SerializerOptions (JsonSerializerOptions) replaces Options.JsonSerializer + Options.JsonSerializerSettings. - TypeConverter is now a JsonConverterFactory registered in SerializerOptions (STJ doesn't inherit class-level [JsonConverter] onto subclasses, so the attribute-only approach silently broke deserialization of all *Settings types). - ObjectFromFieldConverter rewritten as JsonConverterFactory for LookupTable<,>. - New EnumerationJsonConverterFactory bridges [TypeConverter]-attributed types (STJ ignores System.ComponentModel.TypeConverter). Implements ReadAsPropertyName/ WriteAsPropertyName for dictionary-key usage (Xunit2ResultFormat). - ToolOptions.Arguments/Secrets: JToken/JArray/JObject → JsonNode/JsonArray/JsonObject; .Value<T>()/.ToObject<T>() → .GetValue<T>()/.Deserialize<T>(). - DeserializeWithCoercion: narrow Newtonsoft-parity coercion for string→bool, only on the formatter-method path. STJ's native parser handles DateTime/numeric correctly. - NodeToString: permissive equivalent of Newtonsoft's JToken.ToObject<string>(). - Options.Modify deep-clone via JsonSerializer.Serialize/Deserialize. - NpmVersionResolver, NuGetVersionResolver, NuGetPackageResolver, ProcessExtensions all migrated off Newtonsoft. - DelegateHelper.ParseCollection coerces JsonElement → string (STJ deserializes IReadOnlyDictionary<string, object> values as JsonElement, not string). Generator (#118): - DataClassGenerator emits [JsonPropertyName] instead of [JsonProperty]; ToolGenerator imports System.Text.Json.Serialization instead of Newtonsoft.Json. - ToolSerializer: System.Text.Json with a TypeInfoModifier that mirrors the legacy CustomContractResolver — skips read-only properties (except Tool.Schema), empty enumerables, and default-true SettingsClass.ExtensionMethods. Two-pass shape needed because STJ calls the getter BEFORE ShouldSerialize, so the filter alone can't prevent NREs on uninitialized computed properties; we strip read-only JsonPropertyInfo entries at modifier time. - UnsafeRelaxedJsonEscaping to match Newtonsoft's default (no <,>,' escaping). - Models (Tool/Property/DataClass/Enumeration/Task): [JsonRequired] / [JsonPropertyName] / [JsonIgnore]. - All 62 *.Generated.cs tool wrappers regenerated. Source .json files unchanged. Package refs dropped: - Newtonsoft.Json removed from Fallout.Tooling.csproj, Fallout.Tooling.Generator.csproj, and Fallout.Utilities.Text.Json.csproj. Tests: - OptionsTest, ToolOptionsArgumentsTest, SettingsTest migrated to STJ. - TestSerialization Verify snapshot unchanged (parity preserved). - All 425 tests pass across 10 DLLs. Closes #116, #117, #118. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…teTools #117/#118's GenerateTools run re-emitted the GitHubActions workflow YAML and dropped the manual ref:github.head_ref pin that keeps HEAD attached on PR builds. Without it, GitHubTasksTest.GitHubRepositoryFromLocalDirectoryTest fails because repository.Branch resolves to null on detached-merge-SHA checkout. The proper fix (teach the Fallout YAML generator about this override) is tracked in #110. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ChrisonSimtian
added a commit
that referenced
this pull request
May 25, 2026
…the ref pin (#175) The ubuntu-latest workflow YAML carried a manual ref:github.head_ref patch that keeps HEAD attached on PR-triggered builds. Without it, GitHubTasksTest.GitHubRepositoryFromLocalDirectoryTest fails because GitRepository.FromLocalDirectory can't resolve a branch from a detached merge SHA (seen on PR #172 when GenerateTools clobbered the patch and CI went red). Fix: surface the actions/checkout 'ref' input as first-class config on GitHubActionsCheckoutStep + a CheckoutRef setter on [GitHubActions]. The ubuntu-latest attribute in Build.CI.GitHubActions.cs now sets CheckoutRef = "${{ github.head_ref }}", so subsequent GenerateTools runs re-emit the patch automatically instead of dropping it. macos-latest and windows-latest are unchanged — they run on push-to-main where HEAD is already attached on the requested commit. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bundles #117 STJ-4 (Fallout.Tooling engine), #118 STJ-5 (Fallout.Tooling.Generator + model attributes), and the residual #116 tail (Object.ToJObject deletion + Newtonsoft.Json PackageReference removal from Fallout.Utilities.Text.Json) into one PR. After this,
Newtonsoft.Jsonremains in the solution only viaFallout.Common— kept for the *Tasks.cs files until #119 (STJ-6) lands.Why bundled
The two issues are coupled by the engine's
JsonPropertyAttributelookup atOptions.cs:83: the engine maps C# member names → JSON keys via Newtonsoft's[JsonProperty], and the generator emits[JsonProperty]on tool wrappers. Migrating one without the other would either break Discord/Slack/Teams (only 3 of 62 wrappers actually carry[JsonProperty]) or require a compat shim that gets ripped out a week later.Engine changes (#117)
Options.csJObject InternalOptions→JsonObject. NewSerializerOptions(JsonSerializerOptions) replacesJsonSerializer+JsonSerializerSettings.TypeConverterrewritten asJsonConverterFactoryand registered inSerializerOptions.Converters— STJ doesn't inherit class-level[JsonConverter]attributes onto subclasses (this was the subtle bug that took the longest to track down).ObjectFromFieldConverter.csJsonConverterFactory(LookupTable<,> needs polymorphic handling).EnumerationJsonConverter.cs(new)[TypeConverter]-attributed Enumeration subclasses — STJ doesn't honour System.ComponentModel.TypeConverter. IncludesReadAsPropertyName/WriteAsPropertyNamefor dictionary-key usage (Xunit2ResultFormat).ToolOptions.Arguments.cs/.Secrets.cs.Value<T>()/.ToObject<T>()→.GetValue<T>()/.Deserialize<T>(). NewNodeToStringhelper mirrors Newtonsoft's permissiveToObject<string>(STJ throws on non-string JsonValues).DeserializeWithCoercionnarrows string→bool coercion to just the formatter-method path.Options.Modify.csJsonSerializer.Serialize/Deserialize.NpmVersionResolver,NuGetVersionResolver,NuGetPackageResolver,ProcessExtensionsDelegateHelper.csParseCollectioncoerces both string and JsonElement — STJ round-tripsIReadOnlyDictionary<string, object>values as JsonElement, not string.Generator changes (#118)
DataClassGenerator.cs[JsonPropertyName]instead of[JsonProperty].ToolGenerator.csSystem.Text.Json.Serializationinstead ofNewtonsoft.Json.ToolSerializer.csTypeInfoModifierthat mirrors the legacyCustomContractResolver. Two-pass shape (strip read-only properties, then per-propertyShouldSerializefor empty collections + default-trueExtensionMethods) because STJ calls the getter BEFORE invokingShouldSerialize, so a filter alone can't prevent NREs on uninitialized computed properties likeSettingsClass.Name. UsesUnsafeRelaxedJsonEscapingto match Newtonsoft's default.Model/*.cs[JsonRequired]/[JsonPropertyName]/[JsonIgnore]fromSystem.Text.Json.Serialization.All 62
*.Generated.cstool wrappers regenerated. Source.jsonfiles unchanged (the TypeInfoModifier preserves Newtonsoft's null/default omission and escape behaviour byte-for-byte).Package refs dropped
Newtonsoft.Jsonremoved fromFallout.Tooling.csproj,Fallout.Tooling.Generator.csproj, andFallout.Utilities.Text.Json.csproj.Object.ToJObject.csdeleted (closes residual [STJ-3] BREAKING: rewrite Fallout.Utilities.Text.Json on System.Text.Json #116 tail).Fallout.Commonkeeps Newtonsoft.Json until [STJ-6] Migrate Fallout.Common.Tools.*Tasks API integrations #119 (Slack/Twitter/Teams *Tasks.cs).Test plan
dotnet build fallout.slnx— 0 errors (warnings unrelated to this PR).dotnet test fallout.slnx— 425 pass / 0 fail across 10 test DLLs.OptionsTest.TestSerializationVerify snapshot unchanged (STJ round-trip parity preserved).ToolSerializer.Savebyte-for-byte output parity on all 62 tool JSON specifications (no .json diffs after regen).Closes
Unblocks
🤖 Generated with Claude Code