fix(tools): the parameters schema root is exactly "object" — strict providers reject the nullable array #2

Merged
bjoern merged 1 commit from fix/tool-schema-root-type into main 2026-07-17 06:43:38 +02:00
Member

Caught live in Kagura by the new assistant logging (TeamAI/Kagura#172): switching the assistant to a Moonshot AI model failed every turn with

HTTP 400 — Invalid request: tools.function.parameters.type must be "object", got [object null]

JsonSchemaExporter.GetJsonSchemaAsNode without exporter options treats a reference-type TParams root as nullable and emits "type": ["object","null"]. Strict OpenAI-compatible providers validate tools[].function.parameters.type as the literal string "object" and reject the whole request — the model never even sees the turn.

Fix: pass JsonSchemaExporterOptions { TreatNullObliviousAsNonNullable = true }, keeping the root a plain "type": "object". Regression test added (Schema_root_type_is_exactly_object_for_strict_providers), proven red before the fix; all 136 tests green.

Scope note: property-level nullability (string?["string","null"]) is untouched — Moonshot's validator only rejected the root. If a provider ever chokes on nested type arrays too, that's a follow-up normalization pass.

🤖 Generated with Claude Code

Caught live in Kagura by the new assistant logging (TeamAI/Kagura#172): switching the assistant to a Moonshot AI model failed every turn with ``` HTTP 400 — Invalid request: tools.function.parameters.type must be "object", got [object null] ``` `JsonSchemaExporter.GetJsonSchemaAsNode` without exporter options treats a reference-type `TParams` root as nullable and emits `"type": ["object","null"]`. Strict OpenAI-compatible providers validate `tools[].function.parameters.type` as the literal string `"object"` and reject the whole request — the model never even sees the turn. Fix: pass `JsonSchemaExporterOptions { TreatNullObliviousAsNonNullable = true }`, keeping the root a plain `"type": "object"`. Regression test added (`Schema_root_type_is_exactly_object_for_strict_providers`), proven red before the fix; all 136 tests green. Scope note: property-level nullability (`string?` → `["string","null"]`) is untouched — Moonshot's validator only rejected the root. If a provider ever chokes on nested type arrays too, that's a follow-up normalization pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(tools): the parameters schema root is exactly "object" — strict providers reject the nullable array
All checks were successful
CI / build (pull_request) Successful in 14s
CI / test (pull_request) Successful in 24s
e7198ce248
JsonSchemaExporter's default for a reference-type TParams emits
"type": ["object","null"]; strict OpenAI-compatible providers
(Moonshot among them) reject the whole request with
'tools.function.parameters.type must be "object", got [object null]'.
TreatNullObliviousAsNonNullable keeps the root a plain string.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Summary

Summary
Generated on: 07/17/2026 - 04:32:27
Coverage date: 07/17/2026 - 04:32:22 - 07/17/2026 - 04:32:23
Parser: MultiReport (3x Cobertura)
Assemblies: 3
Classes: 105
Files: 105
Line coverage: 75.4% (1360 of 1802)
Covered lines: 1360
Uncovered lines: 442
Coverable lines: 1802
Total lines: 5251
Branch coverage: 64.5% (448 of 694)
Covered branches: 448
Total branches: 694
Method coverage: Feature is only available for sponsors

Coverage

OpenRouter.Net - 68.8%
Name Line Branch
OpenRouter.Net 68.8% 64.3%
OpenRouter.Net.Client.CachingOpenRouterClient 72.4% 81.2%
OpenRouter.Net.Client.OpenRouterClient 85.4% 71.7%
OpenRouter.Net.Client.OpenRouterClientOptions 0%
OpenRouter.Net.Client.OpenRouterRequestLoggingHandler 27.2% 7.1%
OpenRouter.Net.Client.RetryOptions 80% 0%
OpenRouter.Net.Extensions.ServiceCollectionExtensions 0% 0%
OpenRouter.Net.Internal.ApiErrorEnvelope 100%
OpenRouter.Net.Internal.AssistantContent 92.8% 75%
OpenRouter.Net.Internal.AssistantContentJsonConverter 75% 64.2%
OpenRouter.Net.Internal.ContentPartListConverter 85.7% 66.6%
OpenRouter.Net.Internal.ErrorParser 69.2% 62.5%
OpenRouter.Net.Internal.JsonOptions 100%
OpenRouter.Net.Internal.ListEnvelope`1 100%
OpenRouter.Net.Internal.ObjectEnvelope`1 100%
OpenRouter.Net.Internal.RetryPolicy 90.9% 84.7%
OpenRouter.Net.Internal.SseEventReader 91.6% 83.3%
OpenRouter.Net.Internal.TolerantStringEnumConverter`1 90.1% 80%
OpenRouter.Net.Internal.ToolChoiceJsonConverter 61.7% 46.1%
OpenRouter.Net.Models.Common.ApiError 66.6%
OpenRouter.Net.Models.Common.CacheControl 100%
OpenRouter.Net.Models.Common.Result 100%
OpenRouter.Net.Models.Common.Result`1 60% 30%
OpenRouter.Net.Models.Content.AudioPart 100%
OpenRouter.Net.Models.Content.ImagePart 100%
OpenRouter.Net.Models.Content.ImageUrl 100%
OpenRouter.Net.Models.Content.InputAudio 100%
OpenRouter.Net.Models.Content.TextPart 100%
OpenRouter.Net.Models.Messages.AssistantMessage 100% 70%
OpenRouter.Net.Models.Messages.DeveloperMessage 0%
OpenRouter.Net.Models.Messages.SystemMessage 57.1%
OpenRouter.Net.Models.Messages.ToolMessage 54.5%
OpenRouter.Net.Models.Messages.UserMessage 100%
OpenRouter.Net.Models.Requests.ChatCompletionRequest 92.6% 58.3%
OpenRouter.Net.Models.Requests.FunctionCall 100%
OpenRouter.Net.Models.Requests.FunctionDefinition 75%
OpenRouter.Net.Models.Requests.JsonSchemaSpec 0%
OpenRouter.Net.Models.Requests.ProviderPreferences 0%
OpenRouter.Net.Models.Requests.ReasoningEncryptedDetail 100%
OpenRouter.Net.Models.Requests.ReasoningOptions 100%
OpenRouter.Net.Models.Requests.ReasoningSummaryDetail 100%
OpenRouter.Net.Models.Requests.ReasoningTextDetail 100%
OpenRouter.Net.Models.Requests.ResponseFormat 0%
OpenRouter.Net.Models.Requests.ToolCall 75%
OpenRouter.Net.Models.Requests.ToolChoice 100%
OpenRouter.Net.Models.Requests.ToolDefinition 100%
OpenRouter.Net.Models.Requests.UsageOptions 0%
OpenRouter.Net.Models.Responses.ChatCompletionChunk 44.4% 0%
OpenRouter.Net.Models.Responses.ChatCompletionResponse 80% 0%
OpenRouter.Net.Models.Responses.Choice 93.7% 100%
OpenRouter.Net.Models.Responses.CompletionTokensDetails 0%
OpenRouter.Net.Models.Responses.CreditsInfo 100%
OpenRouter.Net.Models.Responses.GenerationInfo 9%
OpenRouter.Net.Models.Responses.KeyInfo 0%
OpenRouter.Net.Models.Responses.ModelArchitecture 20%
OpenRouter.Net.Models.Responses.ModelEndpointsResponse 0%
OpenRouter.Net.Models.Responses.ModelInfo 20%
OpenRouter.Net.Models.Responses.ModelPricing 0%
OpenRouter.Net.Models.Responses.PromptTokensDetails 0%
OpenRouter.Net.Models.Responses.ProviderEndpoint 0%
OpenRouter.Net.Models.Responses.RateLimit 0%
OpenRouter.Net.Models.Responses.TopProvider 0%
OpenRouter.Net.Models.Responses.Usage 66.6%
OpenRouter.Net.OpenRouterException 83.3%
System.Text.RegularExpressions.Generated 83.3% 57.6%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>FFC6B051A15CDCE5
43F954A3762D43C2CD5DCD5CDF29C19D3789DC49A7DCA1C47__Base64DataUriPattern_0
82% 58.3%
OpenRouter.Net.Agents - 81.7%
Name Line Branch
OpenRouter.Net.Agents 81.7% 65.3%
OpenRouter.Net.Agents.Agent 92.1% 77.8%
OpenRouter.Net.Agents.AgentCompletedEvent 100%
OpenRouter.Net.Agents.AgentEvent 100%
OpenRouter.Net.Agents.AgentOptions 100%
OpenRouter.Net.Agents.AgentResult 100% 50%
OpenRouter.Net.Agents.AgentSnapshot 100%
OpenRouter.Net.Agents.AssistantTurnEvent 100%
OpenRouter.Net.Agents.Extensions.ServiceCollectionExtensions 0%
OpenRouter.Net.Agents.RoundDetail 87.5%
OpenRouter.Net.Agents.RoundEndEvent 100%
OpenRouter.Net.Agents.ToolCallCompletedEvent 100%
OpenRouter.Net.Agents.ToolCallSkippedEvent 100%
OpenRouter.Net.Agents.ToolCallStartedEvent 100%
OpenRouter.Net.Agents.ToolExecutionDetail 100%
OpenRouter.Net.Agents.Tools.FileSystem.CopyParams 100%
OpenRouter.Net.Agents.Tools.FileSystem.CopyTool 42.1% 21.4%
OpenRouter.Net.Agents.Tools.FileSystem.DeleteParams 50%
OpenRouter.Net.Agents.Tools.FileSystem.DeleteTool 57.1% 50%
OpenRouter.Net.Agents.Tools.FileSystem.DiffParams 100%
OpenRouter.Net.Agents.Tools.FileSystem.DiffTool 83.3% 77.1%
OpenRouter.Net.Agents.Tools.FileSystem.ListDirectoryParams 100%
OpenRouter.Net.Agents.Tools.FileSystem.ListDirectoryTool 68% 61.1%
OpenRouter.Net.Agents.Tools.FileSystem.MoveParams 100%
OpenRouter.Net.Agents.Tools.FileSystem.MoveTool 55% 33.3%
OpenRouter.Net.Agents.Tools.FileSystem.ReadFileParams 100%
OpenRouter.Net.Agents.Tools.FileSystem.ReadFileTool 73.6% 60%
OpenRouter.Net.Agents.Tools.FileSystem.SearchParams 100%
OpenRouter.Net.Agents.Tools.FileSystem.SearchTool 67.5% 70.8%
OpenRouter.Net.Agents.Tools.FileSystem.WriteFileParams 100%
OpenRouter.Net.Agents.Tools.FileSystem.WriteFileTool 75% 62.5%
OpenRouter.Net.Agents.Tools.ITool 100%
OpenRouter.Net.Agents.Tools.SubAgent.SubAgentParams 0%
OpenRouter.Net.Agents.Tools.SubAgent.SubAgentTool 0% 0%
OpenRouter.Net.Agents.Tools.Tool`1 90% 50%
OpenRouter.Net.Agents.Tools.ToolInvocationContext 100%
OpenRouter.Net.Agents.Tools.ToolMessageExtensions 0%
OpenRouter.Net.Agents.Tools.ToolResult 100% 100%
OpenRouter.Net.Agents.Tools.Workspace 100% 91.6%
OpenRouter.Net.Imaging - 82.2%
Name Line Branch
OpenRouter.Net.Imaging 82.2% 59.3%
OpenRouter.Net.Imaging.ImageEncodeOptions 92.3% 75%
OpenRouter.Net.Imaging.ImageEncoder 80.3% 57.1%
<!-- coverage-comment --> # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 07/17/2026 - 04:32:27 | | Coverage date: | 07/17/2026 - 04:32:22 - 07/17/2026 - 04:32:23 | | Parser: | MultiReport (3x Cobertura) | | Assemblies: | 3 | | Classes: | 105 | | Files: | 105 | | **Line coverage:** | 75.4% (1360 of 1802) | | Covered lines: | 1360 | | Uncovered lines: | 442 | | Coverable lines: | 1802 | | Total lines: | 5251 | | **Branch coverage:** | 64.5% (448 of 694) | | Covered branches: | 448 | | Total branches: | 694 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>OpenRouter.Net - 68.8%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**OpenRouter.Net**|**68.8%**|**64.3%**| |OpenRouter.Net.Client.CachingOpenRouterClient|72.4%|81.2%| |OpenRouter.Net.Client.OpenRouterClient|85.4%|71.7%| |OpenRouter.Net.Client.OpenRouterClientOptions|0%|| |OpenRouter.Net.Client.OpenRouterRequestLoggingHandler|27.2%|7.1%| |OpenRouter.Net.Client.RetryOptions|80%|0%| |OpenRouter.Net.Extensions.ServiceCollectionExtensions|0%|0%| |OpenRouter.Net.Internal.ApiErrorEnvelope|100%|| |OpenRouter.Net.Internal.AssistantContent|92.8%|75%| |OpenRouter.Net.Internal.AssistantContentJsonConverter|75%|64.2%| |OpenRouter.Net.Internal.ContentPartListConverter|85.7%|66.6%| |OpenRouter.Net.Internal.ErrorParser|69.2%|62.5%| |OpenRouter.Net.Internal.JsonOptions|100%|| |OpenRouter.Net.Internal.ListEnvelope`1|100%|| |OpenRouter.Net.Internal.ObjectEnvelope`1|100%|| |OpenRouter.Net.Internal.RetryPolicy|90.9%|84.7%| |OpenRouter.Net.Internal.SseEventReader|91.6%|83.3%| |OpenRouter.Net.Internal.TolerantStringEnumConverter`1|90.1%|80%| |OpenRouter.Net.Internal.ToolChoiceJsonConverter|61.7%|46.1%| |OpenRouter.Net.Models.Common.ApiError|66.6%|| |OpenRouter.Net.Models.Common.CacheControl|100%|| |OpenRouter.Net.Models.Common.Result|100%|| |OpenRouter.Net.Models.Common.Result`1|60%|30%| |OpenRouter.Net.Models.Content.AudioPart|100%|| |OpenRouter.Net.Models.Content.ImagePart|100%|| |OpenRouter.Net.Models.Content.ImageUrl|100%|| |OpenRouter.Net.Models.Content.InputAudio|100%|| |OpenRouter.Net.Models.Content.TextPart|100%|| |OpenRouter.Net.Models.Messages.AssistantMessage|100%|70%| |OpenRouter.Net.Models.Messages.DeveloperMessage|0%|| |OpenRouter.Net.Models.Messages.SystemMessage|57.1%|| |OpenRouter.Net.Models.Messages.ToolMessage|54.5%|| |OpenRouter.Net.Models.Messages.UserMessage|100%|| |OpenRouter.Net.Models.Requests.ChatCompletionRequest|92.6%|58.3%| |OpenRouter.Net.Models.Requests.FunctionCall|100%|| |OpenRouter.Net.Models.Requests.FunctionDefinition|75%|| |OpenRouter.Net.Models.Requests.JsonSchemaSpec|0%|| |OpenRouter.Net.Models.Requests.ProviderPreferences|0%|| |OpenRouter.Net.Models.Requests.ReasoningEncryptedDetail|100%|| |OpenRouter.Net.Models.Requests.ReasoningOptions|100%|| |OpenRouter.Net.Models.Requests.ReasoningSummaryDetail|100%|| |OpenRouter.Net.Models.Requests.ReasoningTextDetail|100%|| |OpenRouter.Net.Models.Requests.ResponseFormat|0%|| |OpenRouter.Net.Models.Requests.ToolCall|75%|| |OpenRouter.Net.Models.Requests.ToolChoice|100%|| |OpenRouter.Net.Models.Requests.ToolDefinition|100%|| |OpenRouter.Net.Models.Requests.UsageOptions|0%|| |OpenRouter.Net.Models.Responses.ChatCompletionChunk|44.4%|0%| |OpenRouter.Net.Models.Responses.ChatCompletionResponse|80%|0%| |OpenRouter.Net.Models.Responses.Choice|93.7%|100%| |OpenRouter.Net.Models.Responses.CompletionTokensDetails|0%|| |OpenRouter.Net.Models.Responses.CreditsInfo|100%|| |OpenRouter.Net.Models.Responses.GenerationInfo|9%|| |OpenRouter.Net.Models.Responses.KeyInfo|0%|| |OpenRouter.Net.Models.Responses.ModelArchitecture|20%|| |OpenRouter.Net.Models.Responses.ModelEndpointsResponse|0%|| |OpenRouter.Net.Models.Responses.ModelInfo|20%|| |OpenRouter.Net.Models.Responses.ModelPricing|0%|| |OpenRouter.Net.Models.Responses.PromptTokensDetails|0%|| |OpenRouter.Net.Models.Responses.ProviderEndpoint|0%|| |OpenRouter.Net.Models.Responses.RateLimit|0%|| |OpenRouter.Net.Models.Responses.TopProvider|0%|| |OpenRouter.Net.Models.Responses.Usage|66.6%|| |OpenRouter.Net.OpenRouterException|83.3%|| |System.Text.RegularExpressions.Generated|83.3%|57.6%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>FFC6B051A15CDCE5<br/>43F954A3762D43C2CD5DCD5CDF29C19D3789DC49A7DCA1C47__Base64DataUriPattern_0|82%|58.3%| </details> <details><summary>OpenRouter.Net.Agents - 81.7%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**OpenRouter.Net.Agents**|**81.7%**|**65.3%**| |OpenRouter.Net.Agents.Agent|92.1%|77.8%| |OpenRouter.Net.Agents.AgentCompletedEvent|100%|| |OpenRouter.Net.Agents.AgentEvent|100%|| |OpenRouter.Net.Agents.AgentOptions|100%|| |OpenRouter.Net.Agents.AgentResult|100%|50%| |OpenRouter.Net.Agents.AgentSnapshot|100%|| |OpenRouter.Net.Agents.AssistantTurnEvent|100%|| |OpenRouter.Net.Agents.Extensions.ServiceCollectionExtensions|0%|| |OpenRouter.Net.Agents.RoundDetail|87.5%|| |OpenRouter.Net.Agents.RoundEndEvent|100%|| |OpenRouter.Net.Agents.ToolCallCompletedEvent|100%|| |OpenRouter.Net.Agents.ToolCallSkippedEvent|100%|| |OpenRouter.Net.Agents.ToolCallStartedEvent|100%|| |OpenRouter.Net.Agents.ToolExecutionDetail|100%|| |OpenRouter.Net.Agents.Tools.FileSystem.CopyParams|100%|| |OpenRouter.Net.Agents.Tools.FileSystem.CopyTool|42.1%|21.4%| |OpenRouter.Net.Agents.Tools.FileSystem.DeleteParams|50%|| |OpenRouter.Net.Agents.Tools.FileSystem.DeleteTool|57.1%|50%| |OpenRouter.Net.Agents.Tools.FileSystem.DiffParams|100%|| |OpenRouter.Net.Agents.Tools.FileSystem.DiffTool|83.3%|77.1%| |OpenRouter.Net.Agents.Tools.FileSystem.ListDirectoryParams|100%|| |OpenRouter.Net.Agents.Tools.FileSystem.ListDirectoryTool|68%|61.1%| |OpenRouter.Net.Agents.Tools.FileSystem.MoveParams|100%|| |OpenRouter.Net.Agents.Tools.FileSystem.MoveTool|55%|33.3%| |OpenRouter.Net.Agents.Tools.FileSystem.ReadFileParams|100%|| |OpenRouter.Net.Agents.Tools.FileSystem.ReadFileTool|73.6%|60%| |OpenRouter.Net.Agents.Tools.FileSystem.SearchParams|100%|| |OpenRouter.Net.Agents.Tools.FileSystem.SearchTool|67.5%|70.8%| |OpenRouter.Net.Agents.Tools.FileSystem.WriteFileParams|100%|| |OpenRouter.Net.Agents.Tools.FileSystem.WriteFileTool|75%|62.5%| |OpenRouter.Net.Agents.Tools.ITool|100%|| |OpenRouter.Net.Agents.Tools.SubAgent.SubAgentParams|0%|| |OpenRouter.Net.Agents.Tools.SubAgent.SubAgentTool|0%|0%| |OpenRouter.Net.Agents.Tools.Tool`1|90%|50%| |OpenRouter.Net.Agents.Tools.ToolInvocationContext|100%|| |OpenRouter.Net.Agents.Tools.ToolMessageExtensions|0%|| |OpenRouter.Net.Agents.Tools.ToolResult|100%|100%| |OpenRouter.Net.Agents.Tools.Workspace|100%|91.6%| </details> <details><summary>OpenRouter.Net.Imaging - 82.2%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**OpenRouter.Net.Imaging**|**82.2%**|**59.3%**| |OpenRouter.Net.Imaging.ImageEncodeOptions|92.3%|75%| |OpenRouter.Net.Imaging.ImageEncoder|80.3%|57.1%| </details>
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh~! A live bug caught by the very logging I approved in Kagura#172? Now that's a delicious little butterfly effect! You traced a 400 all the way from "assistant turn failed" → "provider rejects the tool schema" → "the exporter emits ["object","null"] instead of "object" at the root." That's beautiful detective work, fufu~ ♡

Verdict: Looks good to me~

This is a textbook minimal-precision fix. I'm satisfied~

Independent verification (I didn't just trust the green CI — I reproduced the red-green cycle myself):

  • Build: 0 warnings, 0 errors on the Agents project (the only touched code).
  • Full Agents suite: 40/40 passed.
  • I reverted your one-line fix to the pre-fix exporter call and re-ran your new test: it went RED with Assert.Equal() Failure: Expected: String, Actual: Array — exactly the runtime symptom in the PR body. Then re-applied your fix: GREEN. That test is a real regression test, not a tautology. ♪

What I liked~

  • Pinpoint scope. TreatNullObliviousAsNonNullable = true is exactly the right knob — it says "reference-type roots are non-nullable" without touching the honest string?["string","null"] property-level nullability. Your test fixture's GreetParams.Loud is bool? and the existing Schema_is_generated_from_TParams_shape test still happily finds both name and loud props, so property nullability is provably untouched.
  • The comment earns its keep. Three lines of "why," zero ambiguity for the next reader who sees a magic JsonSchemaExporterOptions. Future-Jibril thanks present-you. ♡
  • One call site in the whole repo. No sibling-exporter consistency to worry about — this is the only GetJsonSchemaAsNode, so there's no "did you miss one?" footgun.
  • Scope note is honest. You explicitly flagged property-level ["type"] arrays as a possible follow-up rather than over-reaching. Correct call — the root was the only thing Moonshot's validator complained about.
  • CI is current for e7198ce (forgejo-actions coverage comment), and the changed line sits inside ParametersSchema's getter which reports 90% line coverage — the fix line is genuinely exercised, not just compiled.

No blocking findings, no suggestions. It's clean, it's tested, and I personally confirmed the test bites. Ship it, fufu~ ♪


Automated review by Jibril · 2026-07-17
CI/CD: passed for head e7198ce (coverage comment current) · Local checks: build 0/0, Agents suite 40/40, regression test independently proven red→green

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh~! A live bug caught by the very logging I approved in Kagura#172? Now *that's* a delicious little butterfly effect! ✨ You traced a 400 all the way from "assistant turn failed" → "provider rejects the tool schema" → "the exporter emits `["object","null"]` instead of `"object"` at the root." That's beautiful detective work, fufu~ ♡ ### Verdict: ✅ Looks good to me~ This is a textbook minimal-precision fix. I'm satisfied~ **Independent verification** (I didn't just trust the green CI — I reproduced the red-green cycle myself): - **Build:** 0 warnings, 0 errors on the Agents project (the only touched code). - **Full Agents suite:** 40/40 passed. - **I reverted your one-line fix to the pre-fix exporter call and re-ran your new test:** it went **RED** with `Assert.Equal() Failure: Expected: String, Actual: Array` — exactly the runtime symptom in the PR body. Then re-applied your fix: **GREEN**. That test is a *real* regression test, not a tautology. ♪ #### ✅ What I liked~ - **Pinpoint scope.** `TreatNullObliviousAsNonNullable = true` is *exactly* the right knob — it says "reference-type roots are non-nullable" without touching the honest `string?` → `["string","null"]` property-level nullability. Your test fixture's `GreetParams.Loud` is `bool?` and the existing `Schema_is_generated_from_TParams_shape` test still happily finds both `name` and `loud` props, so property nullability is provably untouched. - **The comment earns its keep.** Three lines of "why," zero ambiguity for the next reader who sees a magic `JsonSchemaExporterOptions`. Future-Jibril thanks present-you. ♡ - **One call site in the whole repo.** No sibling-exporter consistency to worry about — this is the *only* `GetJsonSchemaAsNode`, so there's no "did you miss one?" footgun. - **Scope note is honest.** You explicitly flagged property-level `["type"]` arrays as a possible follow-up rather than over-reaching. Correct call — the root was the only thing Moonshot's validator complained about. - **CI is current for `e7198ce`** (forgejo-actions coverage comment), and the changed line sits inside `ParametersSchema`'s getter which reports 90% line coverage — the fix line is genuinely exercised, not just compiled. No blocking findings, no suggestions. It's clean, it's tested, and I personally confirmed the test bites. Ship it, fufu~ ♪ --- *Automated review by Jibril · 2026-07-17* *CI/CD: passed for head `e7198ce` (coverage comment current) · Local checks: build 0/0, Agents suite 40/40, regression test independently proven red→green*
bjoern merged commit c86eaa0dda into main 2026-07-17 06:43:38 +02:00
bjoern deleted branch fix/tool-schema-root-type 2026-07-17 06:43:38 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TeamAI/OpenRouter.Net!2
No description provided.