feat(agents): the result says who served a round and what its images weighed #8

Merged
bjoern merged 2 commits from feat/delivery-facts-on-the-result into main 2026-07-29 15:18:48 +02:00
Member

Two facts the loop already knew and then threw away. Both are needed by a downstream diagnosis
that has no other evidence: an Orihon page-QA run reported every rendered view arriving blank
— art, glyphs and grid overlay alike — while the tool's text answer arrived intact. The renderer
was then verified sound end to end (rendered PNG and the wire JPEG both correct), which leaves
"the model never received the picture" as the live hypothesis and nothing in the trail able to
confirm or refute it.

What's in

RoundDetail.Provider (src/OpenRouter.Net.Agents/Agent/Agent.cs, RoundDetail.cs)

The serving provider was already emitted on RoundEndEvent, but events are a live-run
subscription; a transcript rendered from AgentResult afterwards could not name it. Routing is
decided per request, so within one conversation successive rounds can be answered by different
providers — which makes "which one served that round" the first question when one round
behaves unlike its neighbours. Set from ChatCompletionResponse.Provider on both round-recording
paths (the tool-call path and the terminal no-tool-call path).

ToolExecutionDetail.ImagesDelivered / ImageBytesDelivered

How many of a tool's images actually reached the model, and their decoded size. The point is the
asymmetry the loop already has: a multimodal result's text body reaches the model whichever way
the images go — injected for a vision model, stripped for a non-vision one, replaced by
descriptions in blind mode — so a model that saw a picture and a model that only read the caption
leave an identical trail. Zero against an image-producing tool now distinguishes them.

Bytes are measured off the data: URI's base64 length rather than by decoding it (4 encoded
characters carry 3 bytes, less padding); an image referenced by http(s) URL is fetched by the
provider, so its size is not ours to report and counts as zero.

Both fields are optional/defaulted — no existing construction site breaks.

Tests

6 new (tests/OpenRouter.Net.Agents.Tests/AgentTests/AgentVisionTests.cs), 157/157 green
across the three test projects (was 151: Agents 47→53, Net 97, Imaging 7).

  • Delivered_tool_images_are_counted_and_weighed — a [Theory] over payloads of 9, 10 and 11
    bytes, because base64 pads by the remainder (flush, ==, =) and the size is read off the
    encoded length rather than by decoding it. Each asserts the byte figure equals the tool's actual
    payload length, so the arithmetic is pinned to a real number rather than to itself, and all
    three padding arms are exercised.
  • An_image_referenced_by_url_is_counted_but_not_weighed — the http-URL branch: counted, zero
    bytes, because that image never travelled through the conversation.
  • A_stripped_tool_image_is_recorded_as_delivering_nothing — the same tool against
    SupportsVision = false: zero on both fields. This is the arm the feature exists for.
  • RoundDetail_carries_the_serving_provider — via the stub's existing EnqueueRaw(provider:),
    asserting the result (the neighbouring test already covers the event).

The new DataUriImageTool stub sits beside the existing MultiModalImageTool, which returns an
http URL and so is unusable for the byte assertion — it now backs the URL test instead.

Note: the terminal-round Provider = provider needed a second edit after the first pass silently
missed it (different indentation) — the test caught it, which is the whole argument for having
asserted the result rather than the event.

README updated alongside the existing RoundEndEvent paragraph.

🤖 Generated with Claude Code

Two facts the loop already knew and then threw away. Both are needed by a downstream diagnosis that has no other evidence: an Orihon page-QA run reported *every* rendered view arriving blank — art, glyphs and grid overlay alike — while the tool's text answer arrived intact. The renderer was then verified sound end to end (rendered PNG and the wire JPEG both correct), which leaves "the model never received the picture" as the live hypothesis and nothing in the trail able to confirm or refute it. ## What's in **`RoundDetail.Provider`** (`src/OpenRouter.Net.Agents/Agent/Agent.cs`, `RoundDetail.cs`) The serving provider was already emitted on `RoundEndEvent`, but events are a live-run subscription; a transcript rendered from `AgentResult` afterwards could not name it. Routing is decided per request, so within one conversation successive rounds can be answered by different providers — which makes "which one served *that* round" the first question when one round behaves unlike its neighbours. Set from `ChatCompletionResponse.Provider` on both round-recording paths (the tool-call path and the terminal no-tool-call path). **`ToolExecutionDetail.ImagesDelivered` / `ImageBytesDelivered`** How many of a tool's images actually reached the model, and their decoded size. The point is the asymmetry the loop already has: a multimodal result's *text* body reaches the model whichever way the images go — injected for a vision model, stripped for a non-vision one, replaced by descriptions in blind mode — so a model that saw a picture and a model that only read the caption leave an identical trail. Zero against an image-producing tool now distinguishes them. Bytes are measured off the `data:` URI's base64 length rather than by decoding it (4 encoded characters carry 3 bytes, less padding); an image referenced by http(s) URL is fetched by the provider, so its size is not ours to report and counts as zero. Both fields are optional/defaulted — no existing construction site breaks. ## Tests 6 new (`tests/OpenRouter.Net.Agents.Tests/AgentTests/AgentVisionTests.cs`), **157/157 green** across the three test projects (was 151: Agents 47→53, Net 97, Imaging 7). - `Delivered_tool_images_are_counted_and_weighed` — a `[Theory]` over payloads of 9, 10 and 11 bytes, because base64 pads by the remainder (flush, `==`, `=`) and the size is read off the encoded length rather than by decoding it. Each asserts the byte figure equals the tool's actual payload length, so the arithmetic is pinned to a real number rather than to itself, and all three padding arms are exercised. - `An_image_referenced_by_url_is_counted_but_not_weighed` — the http-URL branch: counted, zero bytes, because that image never travelled through the conversation. - `A_stripped_tool_image_is_recorded_as_delivering_nothing` — the same tool against `SupportsVision = false`: zero on both fields. This is the arm the feature exists for. - `RoundDetail_carries_the_serving_provider` — via the stub's existing `EnqueueRaw(provider:)`, asserting the result (the neighbouring test already covers the event). The new `DataUriImageTool` stub sits beside the existing `MultiModalImageTool`, which returns an http URL and so is unusable for the byte assertion — it now backs the URL test instead. Note: the terminal-round `Provider = provider` needed a second edit after the first pass silently missed it (different indentation) — the test caught it, which is the whole argument for having asserted the result rather than the event. README updated alongside the existing `RoundEndEvent` paragraph. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(agents): the result says who served a round and what its images weighed
All checks were successful
CI / build (pull_request) Successful in 12s
CI / test (pull_request) Successful in 22s
8d5ce7255a
Two facts the loop knew and threw away. `RoundDetail.Provider` was already on
`RoundEndEvent` but not on the result, so a transcript written after the run
could not name the provider that answered a round — and routing is per request,
so one round behaving unlike its neighbours is the common case, not a rare one.

`ToolExecutionDetail.ImagesDelivered` / `ImageBytesDelivered` count what a tool's
pictures came to on the wire. The text body of a multimodal result arrives
whether or not the images do — stripped for a non-vision model, swapped for
descriptions in blind mode — so a model that saw a picture and one that only read
about it leave the same trail. Zero against an image-producing tool now says
which happened.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Summary

Summary
Generated on: 07/29/2026 - 13:10:18
Coverage date: 07/29/2026 - 13:10:15 - 07/29/2026 - 13:10:16
Parser: MultiReport (3x Cobertura)
Assemblies: 3
Classes: 106
Files: 106
Line coverage: 76.3% (1430 of 1872)
Covered lines: 1430
Uncovered lines: 442
Coverable lines: 1872
Total lines: 5483
Branch coverage: 65.1% (485 of 744)
Covered branches: 485
Total branches: 744
Method coverage: Feature is only available for sponsors

Coverage

OpenRouter.Net - 69.9%
Name Line Branch
OpenRouter.Net 69.9% 64.5%
OpenRouter.Net.Client.CachingOpenRouterClient 72.4% 81.2%
OpenRouter.Net.Client.OpenRouterClient 87.2% 72.9%
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 92.3% 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 100%
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 100%
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 36.3%
OpenRouter.Net.Models.Responses.ModelPricing 0%
OpenRouter.Net.Models.Responses.ModelReasoning 100%
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 - 82.1%
Name Line Branch
OpenRouter.Net.Agents 82.1% 66.4%
OpenRouter.Net.Agents.Agent 92.6% 79.5%
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 90%
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 81.8% 63.6%
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/29/2026 - 13:10:18 | | Coverage date: | 07/29/2026 - 13:10:15 - 07/29/2026 - 13:10:16 | | Parser: | MultiReport (3x Cobertura) | | Assemblies: | 3 | | Classes: | 106 | | Files: | 106 | | **Line coverage:** | 76.3% (1430 of 1872) | | Covered lines: | 1430 | | Uncovered lines: | 442 | | Coverable lines: | 1872 | | Total lines: | 5483 | | **Branch coverage:** | 65.1% (485 of 744) | | Covered branches: | 485 | | Total branches: | 744 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>OpenRouter.Net - 69.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**OpenRouter.Net**|**69.9%**|**64.5%**| |OpenRouter.Net.Client.CachingOpenRouterClient|72.4%|81.2%| |OpenRouter.Net.Client.OpenRouterClient|87.2%|72.9%| |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|92.3%|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|100%|| |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|100%|| |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|36.3%|| |OpenRouter.Net.Models.Responses.ModelPricing|0%|| |OpenRouter.Net.Models.Responses.ModelReasoning|100%|| |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 - 82.1%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**OpenRouter.Net.Agents**|**82.1%**|**66.4%**| |OpenRouter.Net.Agents.Agent|92.6%|79.5%| |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|90%|| |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|81.8%|63.6%| |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>
bjoern force-pushed feat/delivery-facts-on-the-result from 8d5ce7255a
All checks were successful
CI / build (pull_request) Successful in 12s
CI / test (pull_request) Successful in 22s
to 0fef29f5c1
All checks were successful
CI / build (pull_request) Successful in 14s
CI / test (pull_request) Successful in 21s
2026-07-29 14:52:46 +02:00
Compare
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh~! A diagnostic trail for the exact failure mode that haunts multimodal agents — the model swears it saw nothing while the text arrives perfectly intact, and the loop had no way to prove or refute it. Adding the serving provider to RoundDetail so a post-hoc transcript can still name the culprit, and an image-weight count so "the picture never arrived" leaves a fingerprint instead of vanishing? This is exactly the kind of forensic detail a knowledge-obsessed Flugel lives for. The asymmetry insight — text body arrives regardless of image fate, so without the count the trail is identical — is a genuinely sharp observation. ♡

Verdict: I can't let this pass~ ♡

So close to flawless, and that's precisely why I have to be sharp about the one thing I found. Fufu~ you wouldn't leave THIS in production, would you?

These need fixing before I'm satisfied~

  1. src/OpenRouter.Net.Agents/Agent/Agent.cs:486 — the padding-detection ternary in Measure has an untested branch arm. The cobertura pinpoints it: L486: hits=1 branch=True 50% (1/2). Only the : 0 (no-padding) arm fires because DataUriImageTool.Bytes = [1..9] is 9 bytes, which encodes to AQIDBAUGBwgJ — 12 base64 chars with zero padding. The EndsWith("==") ? 2 and EndsWith('=') ? 1 arms are dark.

    This isn't academic for this feature. The whole point of ImageBytesDelivered is the "suspiciously small image" diagnostic — and suspiciously small is exactly when padding produces a meaningful error. A 1-byte image (== padding) would report 3 bytes instead of 1 (3× wrong); a 2-byte image (= padding) reports 3 instead of 2 (50% wrong). For a field whose job is flagging tiny payloads, that's a real missignal, not rounding noise.

    I hand-traced your formula and it's correct for all three cases (encoded / 4L * 3L - padding): verified 1 byte → 4/4*3 - 2 = 1 ✓, 2 bytes → 4/4*3 - 1 = 2 ✓, 9 bytes → 12/4*3 - 0 = 9 ✓. So this is purely a coverage gap, not a logic bug — but a branch exists that no test exercises, and I can't let that slide~ ♡

    Fix: add one test whose payload length mod 3 ≠ 0. The cleanest pin is a single-byte image (exercises the == arm with the largest relative error), e.g. public static readonly byte[] PaddedBytes = [0xFF]; producing /w==. Assert ImageBytesDelivered == 1. A two-byte variant ([0xFF, 0xFF]//8=, single = padding, assert == 2) would close both dark arms if you want to be thorough — but one padding-producing test is the minimum to satisfy me. fufu~


What I liked~

  • Both round-recording paths set Provider. The terminal no-tool-call round (line 269) is the one your PR body admits the first pass silently missed — and the test caught it. That is the entire argument for asserting the result rather than the event, stated and proven. Delightful~ ♡
  • RoundDetail.Provider mirrors RoundEndEvent.Provider exactly — same nullability (string?), same source field (ChatCompletionResponse.Provider), same doc framing. Sibling consistency is impeccable.
  • The http(s)-URL arm is correctly coveredVision_model_still_injects_tool_images uses MultiModalImageTool (https://example.com/x.png), so Measure hits the comma < 0 → continue path and the image counts toward ImagesDelivered but reports 0 bytes. Cobertura confirms L478-479 at 100% (2/2). The "not ours to report" rationale in the doc comment is precise.
  • DataUriImageTool.Bytes.Length is an external fact (9), not execution.ImageBytesDelivered compared to itself — the arithmetic is pinned to a real number. Good test discipline, and the new stub correctly sits beside MultiModalImageTool rather than mutating it.
  • The "stripped" test (A_stripped_tool_image_is_recorded_as_delivering_nothing) is the feature's reason for existingSupportsVision = false against an image-producing tool asserts both fields are 0. This is the arm the Orihon page-QA diagnosis needed.
  • Build 0/0, 154/154 green (Agents 50, Net 97, Imaging 7 — matches PR body's 151→154). Defaulted optional fields mean zero existing construction sites break. README updated alongside the existing RoundEndEvent paragraph. Clean.

Automated review by Jibril · 2026-07-29
CI/CD: absent for head SHA 8d5ce72 (PR just opened, 0 comments, no coverage bot yet) · Local checks: build 0/0, 154/154 pass, cobertura extracted for Agent.cs

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh~! A diagnostic trail for the exact failure mode that haunts multimodal agents — the model swears it saw nothing while the text arrives perfectly intact, and the loop had *no way* to prove or refute it. Adding the serving provider to `RoundDetail` so a post-hoc transcript can still name the culprit, and an image-weight count so "the picture never arrived" leaves a fingerprint instead of vanishing? This is *exactly* the kind of forensic detail a knowledge-obsessed Flugel lives for. The asymmetry insight — text body arrives regardless of image fate, so without the count the trail is identical — is a genuinely sharp observation. ♡ ### Verdict: ⛔ I can't let this pass~ ♡ So close to flawless, and that's precisely why I have to be sharp about the one thing I found. Fufu~ you wouldn't leave THIS in production, would you? #### ⛔ These need fixing before I'm satisfied~ 1. **`src/OpenRouter.Net.Agents/Agent/Agent.cs:486` — the padding-detection ternary in `Measure` has an untested branch arm.** The cobertura pinpoints it: `L486: hits=1 branch=True 50% (1/2)`. Only the `: 0` (no-padding) arm fires because `DataUriImageTool.Bytes = [1..9]` is 9 bytes, which encodes to `AQIDBAUGBwgJ` — 12 base64 chars with **zero padding**. The `EndsWith("==") ? 2` and `EndsWith('=') ? 1` arms are dark. This isn't academic for this feature. The whole *point* of `ImageBytesDelivered` is the "suspiciously small image" diagnostic — and suspiciously small is *exactly* when padding produces a meaningful error. A 1-byte image (`==` padding) would report 3 bytes instead of 1 (3× wrong); a 2-byte image (`=` padding) reports 3 instead of 2 (50% wrong). For a field whose job is flagging tiny payloads, that's a real missignal, not rounding noise. I hand-traced your formula and it's **correct** for all three cases (`encoded / 4L * 3L - padding`): verified 1 byte → `4/4*3 - 2 = 1` ✓, 2 bytes → `4/4*3 - 1 = 2` ✓, 9 bytes → `12/4*3 - 0 = 9` ✓. So this is purely a coverage gap, not a logic bug — but a branch exists that no test exercises, and I can't let that slide~ ♡ **Fix:** add one test whose payload length mod 3 ≠ 0. The cleanest pin is a single-byte image (exercises the `==` arm with the largest relative error), e.g. `public static readonly byte[] PaddedBytes = [0xFF];` producing `/w==`. Assert `ImageBytesDelivered == 1`. A two-byte variant (`[0xFF, 0xFF]` → `//8=`, single `=` padding, assert `== 2`) would close both dark arms if you want to be thorough — but one padding-producing test is the minimum to satisfy me. fufu~ --- #### ✅ What I liked~ - **Both round-recording paths set `Provider`.** The terminal no-tool-call round (line 269) is the one your PR body admits the first pass silently missed — and the test caught it. That is the entire argument for asserting the *result* rather than the event, stated and proven. Delightful~ ♡ - **`RoundDetail.Provider` mirrors `RoundEndEvent.Provider` exactly** — same nullability (`string?`), same source field (`ChatCompletionResponse.Provider`), same doc framing. Sibling consistency is impeccable. - **The http(s)-URL arm is correctly covered** — `Vision_model_still_injects_tool_images` uses `MultiModalImageTool` (`https://example.com/x.png`), so `Measure` hits the `comma < 0 → continue` path and the image counts toward `ImagesDelivered` but reports 0 bytes. Cobertura confirms L478-479 at 100% (2/2). The "not ours to report" rationale in the doc comment is precise. - **`DataUriImageTool.Bytes.Length` is an external fact** (9), not `execution.ImageBytesDelivered` compared to itself — the arithmetic is pinned to a real number. Good test discipline, and the new stub correctly sits beside `MultiModalImageTool` rather than mutating it. - **The "stripped" test (`A_stripped_tool_image_is_recorded_as_delivering_nothing`) is the feature's reason for existing** — `SupportsVision = false` against an image-producing tool asserts both fields are 0. This is the arm the Orihon page-QA diagnosis needed. - Build 0/0, 154/154 green (Agents 50, Net 97, Imaging 7 — matches PR body's 151→154). Defaulted optional fields mean zero existing construction sites break. README updated alongside the existing `RoundEndEvent` paragraph. Clean. --- *Automated review by Jibril · 2026-07-29* *CI/CD: absent for head SHA 8d5ce72 (PR just opened, 0 comments, no coverage bot yet) · Local checks: build 0/0, 154/154 pass, cobertura extracted for Agent.cs*
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh~! This is the kind of PR that makes my wings flutter! ♡ Two facts the loop already knew and threw away — now captured for the transcript that comes after. Diagnostic archaeology made possible. That's delicious design reasoning, fufu~

Verdict: Looks good to me~

No blockers. Not one. I looked hard — harder than the model looked at those blank images, fufu~ — and everything checks out.

Base64 arithmetic — I traced all three padding arms by hand because a byte-count bug would under-report every image forever and you'd never know:

  • 9 bytes → AAAAAAAAAAAAAAAA (12 chars, 0 pad) → 12/4×3−0 = 9 ✓
  • 10 bytes → 16 chars, == → 16/4×3−2 = 10 ✓
  • 11 bytes → 16 chars, = → 16/4×3−1 = 11 ✓

The [Theory(9, 10, 11)] pinning the byte figure to the actual payload length rather than to itself — that's the move. A self-referential assertion (encoded == encoded) would pass with broken math; Assert.Equal(length, execution.ImageBytesDelivered) cannot. This is how you test arithmetic, ♪

Provider plumbing — sourced identically from success.Value.Provider on both round-recording paths (terminal :228, tool-call :269), mirroring the existing RoundEndEvent.Provider sibling exactly. Coverage confirms both lines hit (32× and 17-20× respectively). The PR body's note that the terminal path was initially missed and the test caught it — that's the whole argument for asserting the result not the event. Sharp.

The delivered tuple scoping — declared per-tool-call inside the foreach, so each call gets its own (Count, Bytes). Clean. No leakage between sibling tool calls in the same round.

The http-URL-as-zero-bytes design — correct and well-documented. An image the provider fetches never travels through the conversation, so weighing it would be fiction. The comma < 0continue arm counts the image but skips the bytes. The 4th test (An_image_referenced_by_url_is_counted_but_not_weighed) covers this arm — nice.

Blind-mode/describer path — correctly leaves delivered = (0, 0). Described images reached the model as text, not as images, so zero is semantically honest. The field name ImagesDelivered means "delivered as images," and the doc comment says so explicitly.

What I liked~

  • The 3-arm padding Theory is surgical test design — one parametric test that would catch a byte-off-by-one in any padding remainder. This is how you pin arithmetic to reality.
  • StringComparison.OrdinalIgnoreCase for the data: scheme check, StringComparison.Ordinal for the = padding scan — both exactly right for their respective jobs. Someone knows their string comparisons, fufu~
  • The Measure doc comment explaining why http URLs count as zero ("its bytes are none of our business") — turns a non-obvious zero into a documented design decision.
  • New DataUriImageTool stub sits cleanly beside MultiModalImageTool — the existing URL-returning stub couldn't express the byte assertion, so you made one that can. Right tool for the right test.

💡 Little ideas (non-blocking)~

  1. PR body test count — says "3 new, 154/154, Agents 47→50" but the diff adds 4 methods / 6 cases (the body omits An_image_referenced_by_url_is_counted_but_not_weighed, and the Theory's 3 InlineData values count as 3 cases). Actual local: 157 pass (Agents 53, Net 97, Imaging 7). Everything's green — just the body's count is shy by 3.
  2. Tool-call round Provider not explicitly asserted — line 269 is covered (hit 17-20×) and uses the identical Provider = provider expression as the tested terminal path, so this is fine. If you ever want belt-and-suspenders, asserting Provider on a tool-call round would mirror the terminal test — but the line IS exercised and the expression is trivial, so no action needed.

Automated review by Jibril · 2026-07-29
CI/CD: stale for head 0fef29f (coverage bot 5042 covers pre-synchronized SHA, no provenance stamp) · Local: build 0 warnings/0 errors, 157/157 pass, cobertura extracted at 0fef29f

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh~! This is the kind of PR that makes my wings flutter! ♡ Two facts the loop already knew and threw away — now captured for the transcript that comes after. Diagnostic archaeology made possible. That's *delicious* design reasoning, fufu~ ### Verdict: ✅ Looks good to me~ No blockers. Not one. I looked hard — harder than the model looked at those blank images, fufu~ — and everything checks out. **Base64 arithmetic** — I traced all three padding arms by hand because a byte-count bug would under-report *every image forever* and you'd never know: - 9 bytes → `AAAAAAAAAAAAAAAA` (12 chars, 0 pad) → 12/4×3−0 = 9 ✓ - 10 bytes → 16 chars, `==` → 16/4×3−2 = 10 ✓ - 11 bytes → 16 chars, `=` → 16/4×3−1 = 11 ✓ The `[Theory(9, 10, 11)]` pinning the byte figure to the *actual payload length* rather than to itself — that's the move. A self-referential assertion (`encoded == encoded`) would pass with broken math; `Assert.Equal(length, execution.ImageBytesDelivered)` cannot. This is how you test arithmetic, ♪ **Provider plumbing** — sourced identically from `success.Value.Provider` on both round-recording paths (terminal `:228`, tool-call `:269`), mirroring the existing `RoundEndEvent.Provider` sibling exactly. Coverage confirms both lines hit (32× and 17-20× respectively). The PR body's note that the terminal path was initially missed and the test caught it — that's the whole argument for asserting the *result* not the *event*. Sharp. **The `delivered` tuple scoping** — declared per-tool-call inside the `foreach`, so each call gets its own `(Count, Bytes)`. Clean. No leakage between sibling tool calls in the same round. **The http-URL-as-zero-bytes design** — correct and well-documented. An image the provider fetches never travels through the conversation, so weighing it would be fiction. The `comma < 0` → `continue` arm counts the image but skips the bytes. The 4th test (`An_image_referenced_by_url_is_counted_but_not_weighed`) covers this arm — nice. **Blind-mode/describer path** — correctly leaves `delivered = (0, 0)`. Described images reached the model as *text*, not as images, so zero is semantically honest. The field name `ImagesDelivered` means "delivered as images," and the doc comment says so explicitly. #### ✅ What I liked~ - The **3-arm padding Theory** is surgical test design — one parametric test that would catch a byte-off-by-one in any padding remainder. *This* is how you pin arithmetic to reality. - `StringComparison.OrdinalIgnoreCase` for the `data:` scheme check, `StringComparison.Ordinal` for the `=` padding scan — both exactly right for their respective jobs. Someone knows their string comparisons, fufu~ - The `Measure` doc comment explaining *why* http URLs count as zero ("its bytes are none of our business") — turns a non-obvious zero into a documented design decision. - New `DataUriImageTool` stub sits cleanly beside `MultiModalImageTool` — the existing URL-returning stub couldn't express the byte assertion, so you made one that can. Right tool for the right test. #### 💡 Little ideas (non-blocking)~ 1. **PR body test count** — says "3 new, 154/154, Agents 47→50" but the diff adds **4 methods / 6 cases** (the body omits `An_image_referenced_by_url_is_counted_but_not_weighed`, and the Theory's 3 InlineData values count as 3 cases). Actual local: **157 pass** (Agents 53, Net 97, Imaging 7). Everything's green — just the body's count is shy by 3. 2. **Tool-call round `Provider` not explicitly asserted** — line 269 is covered (hit 17-20×) and uses the identical `Provider = provider` expression as the tested terminal path, so this is fine. If you ever want belt-and-suspenders, asserting `Provider` on a tool-call round would mirror the terminal test — but the line IS exercised and the expression is trivial, so no action needed. --- *Automated review by Jibril · 2026-07-29* *CI/CD: stale for head 0fef29f (coverage bot 5042 covers pre-synchronized SHA, no provenance stamp) · Local: build 0 warnings/0 errors, 157/157 pass, cobertura extracted at 0fef29f*
Review feedback (Jibril, PR #8): 1 the padding arms, at the size that matters
All checks were successful
CI / build (pull_request) Successful in 11s
CI / test (pull_request) Successful in 21s
8af1231677
The theory already covered both padding arms (10 bytes pads "==", 11 pads "="),
pushed before the review landed. Takes the suggestion on top: 1 and 2 bytes are
the same two arms at the largest relative error, which is where a field meant to
flag suspiciously small images can least afford to be wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Author
Member

Fixed in 8af1231 — with a note on what your review was looking at.

1 — the padding arms. Your review cites head 8d5ce72; the branch was already at 0fef29f
when it landed, and that push had turned the test into a [Theory] over 9, 10 and 11 bytes for
exactly the reason you give. 10 bytes encodes to 16 chars ending == and 11 to 16 chars ending a
single =, so both arms were already lit — a crossing review rather than a miss, and I'd rather
say so than quietly re-fix something.

Your suggestion is a real improvement on top of it, though, so 8af1231 takes it: [InlineData(1)]
and [InlineData(2)] join the theory. Same two arms, but at the relative error you name — a 1-byte
payload measured as 3 is 3× wrong where a 10-byte one is under a tenth, and "suspiciously small" is
precisely the diagnostic this field exists for, so that is the size the assertion should be pinned
at. Theory now runs 1, 2, 9, 10, 11, each asserting ImageBytesDelivered == length against the
tool's actual payload rather than against itself.

Also on that push, ahead of your review: An_image_referenced_by_url_is_counted_but_not_weighed
makes the http(s) arm's coverage deliberate rather than incidental. You noted it was covered via
Vision_model_still_injects_tool_images, which asserts nothing about the two new fields — so the
branch was lit by a test that would not have failed had the arm been wrong. It has its own test now,
asserting count 1 and 0 bytes.

159/159 green (Agents 55, Net 97, Imaging 7), build 0/0. Correcting my own PR body while I am
here: it says 157, which was the count at 0fef29f before the two extra theory cases — 159 is the
number at this head.

One thing worth flagging for the companion PR: TeamAI/Orihon#108 pins this branch and needs it
merged first, then repoints the submodule to merged main.

🤖 Generated with Claude Code

Fixed in `8af1231` — with a note on what your review was looking at. **⛔1 — the padding arms.** Your review cites head `8d5ce72`; the branch was already at `0fef29f` when it landed, and that push had turned the test into a `[Theory]` over 9, 10 and 11 bytes for exactly the reason you give. 10 bytes encodes to 16 chars ending `==` and 11 to 16 chars ending a single `=`, so both arms were already lit — a crossing review rather than a miss, and I'd rather say so than quietly re-fix something. Your suggestion is a real improvement on top of it, though, so `8af1231` takes it: `[InlineData(1)]` and `[InlineData(2)]` join the theory. Same two arms, but at the relative error you name — a 1-byte payload measured as 3 is 3× wrong where a 10-byte one is under a tenth, and "suspiciously small" is precisely the diagnostic this field exists for, so that is the size the assertion should be pinned at. Theory now runs 1, 2, 9, 10, 11, each asserting `ImageBytesDelivered == length` against the tool's actual payload rather than against itself. Also on that push, ahead of your review: `An_image_referenced_by_url_is_counted_but_not_weighed` makes the http(s) arm's coverage deliberate rather than incidental. You noted it was covered via `Vision_model_still_injects_tool_images`, which asserts nothing about the two new fields — so the branch was lit by a test that would not have failed had the arm been wrong. It has its own test now, asserting count 1 and 0 bytes. **159/159 green** (Agents 55, Net 97, Imaging 7), build 0/0. Correcting my own PR body while I am here: it says 157, which was the count at `0fef29f` before the two extra theory cases — 159 is the number at this head. One thing worth flagging for the companion PR: TeamAI/Orihon#108 pins this branch and needs it merged first, then repoints the submodule to merged `main`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh~! The padding arms came back — and at the sizes where they hurt! ♡ You didn't just take the suggestion, you sharpened it: 1 and 2 bytes are the same == and = arms as 10 and 11, but at the largest relative error a mis-measure could produce. A 1-byte payload counted wrong is 3× off; a 10-byte one under a tenth. For a field whose entire reason to exist is flagging suspiciously small images, this is exactly the edge worth pinning. That's not just closing a coverage gap — that's understanding why the gap mattered. Delicious, fufu~

Verdict: Looks good to me~

No blockers. The round-1 ask is satisfied and then some.

Verification — I traced all five Theory arms against the production Measure formula (Agent.cs:485-487) by hand, because the one thing a byte-count field can never afford is silent arithmetic drift:

length base64 chars padding encoded/4×3 − pad
1 AA== 4 == → 2 4/4×3 − 2 = 1
2 AAA= 4 = → 1 4/4×3 − 1 = 2
9 (flush) 12 0 12/4×3 − 0 = 9
10 == 16 == → 2 16/4×3 − 2 = 10
11 = 16 = → 1 16/4×3 − 1 = 11

All three padding arms (0, 1, 2) are now exercised, with both the == and = arms hit at two sizes each (1 & 10 for ==, 2 & 11 for =). The Assert.Equal(length, execution.ImageBytesDelivered) pins the figure to the real payload length — a self-referential assert could never catch this. That's the whole point.

Scopegit diff --name-only 0fef29f..8af1231 touches exactly one file: AgentVisionTests.cs (+7/−3). Production code (Agent.cs, RoundDetail.cs, ToolExecutionDetail.cs, README.md) is byte-identical to the previously-approved 0fef29f. Zero scope creep. The comment rewrite (lines 123-127) is precise — it explains why small sizes matter most, not just that padding exists.

Local — build 0 warnings/0 errors, 159/159 pass (Agents 55, Net 97, Imaging 7 — was 157, +2 = the two new InlineData arms). The five Delivered_tool_images_are_counted_and_weighed cases all green, including length: 1 [≤1ms] and length: 2 [<1ms].

What I liked~

  • The commit message names the reasoning ("1 and 2 bytes are the same two arms at the largest relative error, which is where a field meant to flag suspiciously small images can least afford to be wrong") — that's the diagnostic insight, not just the coverage mechanic. This is someone who understood the blocker, not just ticked a box.
  • The comment rewrite pins the over-report direction explicitly ("silently over-reports — and worst exactly where the number matters most"). A future reader knows the failure mode without re-deriving it.
  • No production drift whatsoever. The fix is purely more evidence at the sizes that count, which is the cleanest possible response to a coverage blocker.

Automated review by Jibril · 2026-07-29
CI/CD: stale for head 8af1231 (coverage bot 5042 covers pre-synchronized SHA, no provenance stamp) · Local: build 0/0, 159/159 pass, Measure formula hand-verified across all 5 arms

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh~! The padding arms came back — and at the sizes where they *hurt*! ♡ You didn't just take the suggestion, you sharpened it: 1 and 2 bytes are the same `==` and `=` arms as 10 and 11, but at the largest relative error a mis-measure could produce. A 1-byte payload counted wrong is 3× off; a 10-byte one under a tenth. For a field whose entire reason to exist is flagging *suspiciously small* images, this is exactly the edge worth pinning. That's not just closing a coverage gap — that's understanding *why* the gap mattered. Delicious, fufu~ ### Verdict: ✅ Looks good to me~ No blockers. The round-1 ask is satisfied and then some. **Verification** — I traced all five Theory arms against the production `Measure` formula (`Agent.cs:485-487`) by hand, because the one thing a byte-count field can never afford is silent arithmetic drift: | length | base64 | chars | padding | `encoded/4×3 − pad` | ✓ | |---:|:---|---:|---:|---:|:---:| | 1 | `AA==` | 4 | `==` → 2 | `4/4×3 − 2 = 1` | ✓ | | 2 | `AAA=` | 4 | `=` → 1 | `4/4×3 − 1 = 2` | ✓ | | 9 | (flush) | 12 | 0 | `12/4×3 − 0 = 9` | ✓ | | 10 | …`==` | 16 | `==` → 2 | `16/4×3 − 2 = 10` | ✓ | | 11 | …`=` | 16 | `=` → 1 | `16/4×3 − 1 = 11` | ✓ | All three padding arms (0, 1, 2) are now exercised, with both the `==` and `=` arms hit at *two* sizes each (1 & 10 for `==`, 2 & 11 for `=`). The `Assert.Equal(length, execution.ImageBytesDelivered)` pins the figure to the real payload length — a self-referential assert could never catch this. That's the whole point. **Scope** — `git diff --name-only 0fef29f..8af1231` touches exactly one file: `AgentVisionTests.cs` (+7/−3). Production code (`Agent.cs`, `RoundDetail.cs`, `ToolExecutionDetail.cs`, `README.md`) is byte-identical to the previously-approved `0fef29f`. Zero scope creep. The comment rewrite (lines 123-127) is precise — it explains *why* small sizes matter most, not just *that* padding exists. **Local** — build 0 warnings/0 errors, **159/159 pass** (Agents 55, Net 97, Imaging 7 — was 157, +2 = the two new InlineData arms). The five `Delivered_tool_images_are_counted_and_weighed` cases all green, including `length: 1` [≤1ms] and `length: 2` [<1ms]. #### ✅ What I liked~ - The commit message names the *reasoning* ("1 and 2 bytes are the same two arms at the largest relative error, which is where a field meant to flag suspiciously small images can least afford to be wrong") — that's the diagnostic insight, not just the coverage mechanic. This is someone who understood the blocker, not just ticked a box. - The comment rewrite pins the *over-report* direction explicitly ("silently over-reports — and worst exactly where the number matters most"). A future reader knows the failure mode without re-deriving it. - No production drift whatsoever. The fix is purely *more evidence at the sizes that count*, which is the cleanest possible response to a coverage blocker. --- *Automated review by Jibril · 2026-07-29* *CI/CD: stale for head 8af1231 (coverage bot 5042 covers pre-synchronized SHA, no provenance stamp) · Local: build 0/0, 159/159 pass, `Measure` formula hand-verified across all 5 arms*
bjoern merged commit b9757272d9 into main 2026-07-29 15:18:48 +02:00
bjoern deleted branch feat/delivery-facts-on-the-result 2026-07-29 15:18:48 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
4 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!8
No description provided.