Conversation
…4054) Brighter's JSON serialization camelCases header bag keys, so a "SessionId" key returns as "sessionId" after an Outbox round-trip. The publisher's exact-match lookup missed it (session never set) and the case-sensitive reserved-header filter let it leak into ApplicationProperties. Both lookups are now OrdinalIgnoreCase. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Our agent can fix these. Install it.
Gates Passed
4 Quality Gates Passed
Quality Gate Profile: Clean Code Collective
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.
|
@Jonny-Freemarket Any thoughts? |
Code Review — PR #4205: case-insensitive SessionId & reserved-header handling (ASB)Reviewed the diff against the surrounding code and traced the serialization path. Overall: correct, well-scoped fix with an accurate root-cause diagnosis. I verified the claim independently — Correctness ✅
Suggestions (non-blocking)
Conventions ✅
Nice fix — the one thing I'd genuinely push on before merge is adding the reserved-header-leak assertions (suggestion #1) so the second half of the change is actually covered by a test. 🤖 Generated with Claude Code |
|
A note to self here, I wonder if the issue is more that we need to look at the options, which the user could override, and use those when we look into the bag. Otherwise, if someone changes the JsonNamingPolicy, we can't be sure that we won't have a different bug |
Code Review — PR #4205: case-insensitive SessionId & reserved-header handling for ASBReviewed against ✅ Root cause is confirmed by the codeThe PR's diagnosis is accurate. var convertedPropName = options.PropertyNamingPolicy?.ConvertName(propertyName) ?? propertyName;with ✅ Fix is a robustness improvement, not just a renameWorth calling out: the new code is strictly safer than the original for the null case. The old path did ✅ Scoping is correct — the consumer side is unaffectedI checked 🟡 Minor observations (non-blocking)
Style / conventions
Verdict: approve. Correct root cause, minimal targeted fix, added test, no regressions to the read path. The minor points above are optional. 🤖 Generated with Claude Code |
… policy (#4054) The previous fix matched bag keys case-insensitively, which only works for the default camelCase policy (camelCase differs from PascalCase by leading case only). Brighter's Outbox serializes bag keys with JsonSerialisationOptions.Options, whose PropertyNamingPolicy is user-overridable — under e.g. SnakeCaseLower a "SessionId" key round-trips as "session_id" and the case-insensitive match fails again, dropping the session and leaking reserved keys into ApplicationProperties. Resolve keys using the same naming policy that serialized them: ASBConstants.IsBagKey matches the raw key or the key as the active PropertyNamingPolicy renders it (read at call time so a later Options override is honoured), case-insensitive on top. The publisher uses IsBagKey/IsReservedHeader for the SessionId lookup and reserved-header filter. Test round-trips the Bag through JsonSerialisationOptions.Options exactly as the Outbox does, across camelCase, snake_case, and no policy, isolated in a parallelism-disabled collection that restores Options. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Code Review — PR #4205 (fix: case-insensitive SessionId & reserved-header handling for ASB, #4054)Overall this is a well-diagnosed, well-tested fix. The root-cause analysis (camelCase A few observations, mostly minor: Correctness
Performance
Scope
Tests
Nice, focused fix with a diagnosis-first approach. 👍 (Automated review by Claude) |
…righterCommand#4151) DictionaryStringObjectJsonConverter applied JsonSerialisationOptions' PropertyNamingPolicy (camelCase) to dictionary keys as well as property names, so a bag key written as "SessionId" was serialized as "sessionId". Bag keys are arbitrary user-defined identifiers, not C# property names, so consumers that look the key up by its original name (the ASB SessionId lookup in BrighterCommand#4054, and any Python Lambda reading a PascalCase key off an SNS notification per BrighterCommand#4132) silently missed it. Root cause of the BrighterCommand#4054-class bugs: stop applying the naming policy to dictionary keys and write them verbatim. PropertyNamingPolicy still governs real MessageHeader property names, so the wire format for properties is unchanged; only Bag keys now round-trip as written. This addresses the whole class of read-side workarounds (the policy-aware lookup in BrighterCommand#4205 stays as belt-and-braces for messages already persisted under the old camelCasing during an upgrade drain). The origin of the key mutation was traced to the converter's introduction (BrighterCommand#1531), where it was copied from the reference implementation it is based on rather than added to fix a specific Brighter problem. Tests: - new BagKeyRoundTripTests pins that PascalCase and lowercase bag keys survive verbatim. - the CloudEvents subject-migration test previously characterised the bug ("converts to camelCase"); it now asserts the key is preserved, matching the fix. The Header.Subject guidance from BrighterCommand#4132 remains valid and its tests are unchanged. Closes BrighterCommand#4151 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Fixes #4054 — an ASB
SessionIdset from a Brighter Outbox was silently dropped, and camelCased reserved keys leaked intoApplicationProperties.Root cause
Brighter's Outbox serializes the header
BagwithJsonSerialisationOptions.Options, whosePropertyNamingPolicyrewrites bag keys (seeDictionaryStringObjectJsonConverter.Write). Under the defaultJsonNamingPolicy.CamelCase, a key written as"SessionId"comes back as"sessionId"after a round-trip.In
AzureServiceBusMessagePublisher:Bag.TryGetValue("SessionId", …)is an exact match, so it missed the camelCased"sessionId"key and the session was never set on the outgoing message.ReservedHeaders.Contains(h.Key)was also case-sensitive, so the camelCased reserved keys (sessionId,messageType,handledCount,replyTo) leaked intoApplicationProperties.Fix
Resolve bag keys using the same naming policy that serialized them, rather than a fixed case comparison.
A first pass used
OrdinalIgnoreCase, but that only works for the default camelCase policy (camelCase differs from PascalCase by leading case only).JsonSerialisationOptions.Optionshas a public setter, so a user can overridePropertyNamingPolicy— under e.g.SnakeCaseLower,"SessionId"round-trips as"session_id"and a case-insensitive match fails again, reintroducing the bug.ASBConstants.IsBagKey/IsReservedHeadernow match a bag key against either the raw reserved key or the key as the activePropertyNamingPolicyrenders it (read at call time, so a laterOptionsoverride is honoured), case-insensitive on top. The publisher uses these for both theSessionIdlookup and the reserved-header filter. This is correct under camelCase, snake_case, a custom policy, or none.Tests
When_Converting_A_Message_With_A_Session_Id.csis now a[Theory]overCamelCase,SnakeCaseLower, and no policy. Each case round-trips theBagthroughJsonSerialisationOptions.Optionsexactly as the Outbox does (serialize → deserialize), so the key is mangled by the real policy rather than a hand-written string, then asserts theSessionIdis set and the reserved key is kept out ofApplicationProperties. The tests live in a parallelism-disabled collection that snapshots and restoresOptionsin afinally, so mutating the global policy can't leak into other tests. TheSnakeCaseLowercase fails the earlierOrdinalIgnoreCaseapproach and passes the policy-aware one.Scope / follow-up
Kept to the ASB publisher. A survey found the same exact-match pattern elsewhere; those are tracked in #4214 (RocketMQ
Keys/Tagbroken under camelCase; AWSmessageDeduplicationIdand coreProducerTopiclatent under a non-casing policy;AzureServiceBusSchedulersame smell but not reproducible — it converts an empty freshly-built bag). #4214 also proposes lifting a shared policy-aware lookup intoParamore.Brightercore.🤖 Generated with Claude Code