Skip to content

feat: pass QueryContext into QueryProcessor (#320)#324

Merged
iancooper merged 21 commits into
masterfrom
feature/320-pass-query-context
May 26, 2026
Merged

feat: pass QueryContext into QueryProcessor (#320)#324
iancooper merged 21 commits into
masterfrom
feature/320-pass-query-context

Conversation

@iancooper
Copy link
Copy Markdown
Member

Summary

  • Add IQueryContext queryContext = null parameter to IQueryProcessor.Execute and ExecuteAsync, letting callers supply a context (caller-wins semantics via ??=)
  • Add IPolicyRegistry<string> Policies property to IQueryContext/QueryContext; QueryProcessor constructor accepts and propagates a policy registry via InitQueryContext
  • RetryableQueryDecorator reads policies from Context.Policies instead of Context.Bag
  • QueryLoggingDecorator receives JsonSerializerSettings via constructor injection instead of Context.Bag lookup
  • Builder/DI: AddDefaultPolicies/AddPolicies register IPolicyRegistry<string> as singleton; AddJsonQueryLogging registers JsonSerializerSettings as singleton — both via a new PolicyDIExtensions/QueryLoggingDIExtensions pattern
  • Remove AddContextBagItem, DarkerContextBag, _contextBagData, NewtonsoftJsonSerializer, Constants.ContextBagKey
  • FakeQueryProcessor gains LastProvidedContext property

Test plan

  • All 77 unit tests pass on net8 and net9
  • All 6 AOT tests pass on net8 and net9
  • Build produces 0 warnings, 0 errors
  • No references to removed APIs remain in src/

🤖 Generated with Claude Code

iancooper and others added 21 commits May 20, 2026 10:01
…ocessor (#320)

Requirements spec for adding optional IQueryContext parameter to IQueryProcessor.Execute
and ExecuteAsync, following Brighter's InitRequestContext pattern. Four rounds of
adversarial review completed and all findings resolved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ADR 0010, approved tasks, review files, and updated requirements
for the QueryContext parameter feature.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…320)

Phase 2: Update IQueryProcessor.Execute and QueryProcessor.Execute to accept
optional IQueryContext parameter. When null, context is created inline from
factory (replacing CreateQueryContext). Initialize QueryContext.Bag to empty
dictionary. Update FakeQueryProcessor to implement new interface signature.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Regression guard: when a caller passes an IQueryContext to Execute,
the handler receives that exact instance with its Bag entries intact.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Token (#320)

Phase 2: Update IQueryProcessor.ExecuteAsync and QueryProcessor.ExecuteAsync
to accept optional IQueryContext parameter (before CancellationToken). When
null, context is created inline from factory. Update FakeQueryProcessor to
implement new interface signature.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…320)

Regression guard: when a caller passes an IQueryContext to ExecuteAsync,
the handler receives that exact instance with its Bag entries intact.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…#320)

Both Execute and ExecuteAsync now create context inline; CreateQueryContext()
is no longer called. Remove it along with the unused System.Linq import.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eryProcessor (#320)

Phase 2: QueryProcessor accepts optional IPolicyRegistry<string> in constructor.
InitQueryContext sets context.Policies from the registry when the caller hasn't
provided one (caller-wins: ??= semantics).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…xt (#320)

Regression guard: when caller's context already has Policies set, InitQueryContext
(??= semantics) must not overwrite them with the processor's registry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace Context.Bag lookup with Context.Policies typed property in both
sync and async RetryableQueryDecorator, throwing ConfigurationException
when no registry is configured.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cies null (#320)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ettings (#320)

Replace Context.Bag lookup with constructor-injected JsonSerializerSettings in
both sync and async QueryLoggingDecorator. Move RetryableQueryHandler and
LoggingQueryHandler test doubles to TestDoubles/ per project convention,
reusing SyncTestQuery across policy and logging decorator tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…320)

Inline JsonConvert.SerializeObject directly in both logging decorators,
remove the AddContextBagItem call from AddJsonQueryLogging, and delete
the now-unused NewtonsoftJsonSerializer wrapper class.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add PolicyRegistry property to QueryProcessorBuilder (public for cross-assembly
access from the Policies extension package). Update Policies() and DefaultPolicies()
non-generic methods to store the registry on the builder; update generic
AddPolicies/AddDefaultPolicies to drop the AddContextBagItem call. Remove
ContextBagKey constant from both Policies and QueryLogging packages (no more
callers). Fix nullable annotation syntax for netstandard2.0 compatibility in
logging decorators.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ions (#320)

Remove AddContextBagItem from IQueryProcessorExtensionBuilder, QueryProcessorBuilder,
and ServiceCollectionDarkerHandlerBuilder. The _contextBagData dictionary stays in
QueryProcessorBuilder temporarily until Build() is updated in the next task.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…320)

Update Build() to pass PolicyRegistry instead of _contextBagData; remove
_contextBagData dictionary and contextBagData constructor param from
QueryProcessor. Update ServiceCollectionExtensions to resolve
IPolicyRegistry<string> from DI instead of DarkerContextBag.
Remove DarkerContextBag and update ServiceCollectionDarkerHandlerBuilder.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…320)

Add Policies and QueryLogging references to DI extension project.
Add IServiceCollection Services to IDarkerHandlerBuilder.
Add IDarkerHandlerBuilder.AddAsyncHandlers() for async handler registration.
Add PolicyDIExtensions with DI-specific AddPolicies/AddDefaultPolicies overloads
that register IPolicyRegistry<string> as singleton alongside decorator types.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add QueryLoggingDIExtensions with AddJsonQueryLogging(IDarkerHandlerBuilder) that registers
JsonSerializerSettings as a singleton so DI injects it into QueryLoggingDecorator constructors,
mirroring the PolicyDIExtensions pattern for IPolicyRegistry<string>.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…uteAsync (#320)

Add LastProvidedContext property to FakeQueryProcessor so tests can assert which
IQueryContext was passed to Execute or ExecuteAsync; null when no context is provided.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@codescene-delta-analysis codescene-delta-analysis Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Passed
4 Quality Gates Passed

See analysis details in CodeScene

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.

@iancooper iancooper merged commit f80bef9 into master May 26, 2026
4 checks passed
@iancooper iancooper deleted the feature/320-pass-query-context branch May 26, 2026 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant