Skip to content

Strengthen engine descriptors and typed navigation - #129

Merged
SamuelMcAravey merged 14 commits into
mainfrom
codex/2025-12-21-refactor-solution-for-transport-agnostic-modules-1ziys6
Dec 22, 2025
Merged

Strengthen engine descriptors and typed navigation#129
SamuelMcAravey merged 14 commits into
mainfrom
codex/2025-12-21-refactor-solution-for-transport-agnostic-modules-1ziys6

Conversation

@SamuelMcAravey

Copy link
Copy Markdown
Member

Summary

  • introduce typed engine descriptor interface and generic factories with manifest-driven navigation and security enums
  • add strongly typed navigation tokens and signature algorithm metadata to engine manifests and adapter responses
  • document engine/adapter relationships and update tests and registries for deterministic discovery

Testing

  • dotnet test tests/Bravellian.Platform.Tests/Bravellian.Platform.Tests.csproj --filter EngineRefactoringTests --logger "console;verbosity=minimal"

Codex Task

Copilot AI review requested due to automatic review settings December 21, 2025 05:11

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a strongly-typed, transport-agnostic engine architecture for the Bravellian platform. It establishes a manifest-driven discovery system that decouples business logic engines (UI and webhook) from their transport adapters, enabling modules to declare capabilities, security requirements, and navigation hints through typed descriptors. The changes implement generic factories for type-safe engine resolution and add enums for navigation targets and signature algorithms.

Key Changes

  • Introduced typed engine descriptor interfaces (IModuleEngineDescriptor, ModuleEngineDescriptor<TContract>) with factory-based resolution
  • Added strongly-typed navigation tokens (ModuleNavigationToken, NavigationTargetKind) and security metadata (ModuleSignatureAlgorithm, ModuleEngineSecurity)
  • Implemented discovery service and registry for deterministic engine lookup by module, kind, feature area, or webhook provider/event pairs

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
EngineRefactoringTests.cs Comprehensive test suite validating UI/webhook engine execution, discovery filtering, and adapter behavior
UiEngineAdapter.cs Adapter mapping UI engine contracts to transport-ready responses with navigation tokens
UiAdapterResponse.cs Response record containing view model, navigation targets, and emitted events
FullStackModuleServiceCollectionExtensions.cs Registers ModuleEngineDiscoveryService for full-stack modules
WebhookRequest.cs Request record passed to webhook engines with provider, event type, and payload
WebhookOutcomeType.cs Enum defining webhook processing outcomes (Acknowledge, Retry, EnqueueEvent)
WebhookOutcome.cs Outcome record with factory methods for webhook engine results
UiEngineResult.cs Result record for UI engines containing view model, navigation, and events
NavigationTargetKind.cs Enum for navigation target types (Route, Dialog, Component, External)
ModuleSignatureAlgorithm.cs Enum for webhook signature algorithms (None, HmacSha256, HmacSha512, RsaSha256)
ModuleRegistry.cs Updated to register engine descriptors during module initialization and validate module key consistency
ModuleNavigationToken.cs Typed navigation token record for engine-to-adapter communication
ModuleEngineWebhookMetadata.cs Webhook event metadata for provider/event type routing
ModuleEngineSecurity.cs Security metadata record for signature validation and idempotency
ModuleEngineSchema.cs Schema hints record for engine input/output types
ModuleEngineRegistry.cs Internal registry for storing and querying engine descriptors by various criteria
ModuleEngineNavigationHints.cs Navigation hints record containing well-known navigation tokens
ModuleEngineManifest.cs Comprehensive manifest record describing engine capabilities, schemas, security, and compatibility
ModuleEngineDiscoveryService.cs Public service for listing, filtering, and resolving engine descriptors and instances
ModuleEngineDescriptor.cs Strongly-typed descriptor record wrapping manifest with factory for engine resolution
ModuleEngineCompatibility.cs Compatibility metadata record for version requirements
ModuleEngineCapabilities.cs Capabilities record declaring supported actions and events
ModuleEngineAdapterHints.cs Adapter hints record for transport-level requirements
IWebhookEngine.cs Generic webhook engine interface for handling typed payloads
IUiEngine.cs Generic UI engine interface for executing commands and producing view models
IModuleEngineDescriptor.cs Base interface for engine descriptors with module key, manifest, and factory
IEngineModule.cs Marker interface for modules exposing engine descriptors
EngineKind.cs Enum distinguishing UI and webhook engines
BackgroundModuleServiceCollectionExtensions.cs Registers ModuleEngineDiscoveryService for background modules
WebhookEngineAdapter.cs Adapter validating signatures, enforcing idempotency, and dispatching to webhook engines
WebhookAdapterResponse.cs Response record mapping webhook outcomes to transport responses
WebhookAdapterRequest.cs Request envelope for webhook adapters with headers, signature, and payload
IWebhookSignatureValidator.cs Interface for webhook signature validation implementations
ApiModuleServiceCollectionExtensions.cs Registers ModuleEngineDiscoveryService for API modules
engine-overview.md Documentation of engine architecture, building blocks, adapter roles, and versioning strategy

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineRegistry.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineDiscoveryService.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.FullStack/UiEngineAdapter.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs Outdated
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 21, 2025 05:16
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
SamuelMcAravey and others added 2 commits December 20, 2025 21:18
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@SamuelMcAravey

Copy link
Copy Markdown
Member Author

@copilot Can you resolve the comments on this PR?

Copilot AI commented Dec 21, 2025

Copy link
Copy Markdown
Contributor

@SamuelMcAravey I've opened a new pull request, #130, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 35 out of 35 changed files in this pull request and generated 14 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/Bravellian.Platform.Tests/EngineRefactoringTests.cs
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineCapabilities.cs
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineRegistry.cs
Comment thread docs/engine-overview.md Outdated
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineRegistry.cs Outdated
Comment thread tests/Bravellian.Platform.Tests/EngineRefactoringTests.cs
Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs
Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs Outdated
Comment thread tests/Bravellian.Platform.Tests/EngineRefactoringTests.cs
Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs
@SamuelMcAravey

Copy link
Copy Markdown
Member Author

@copilot Can you address the comments on this PR?

Copilot AI commented Dec 21, 2025

Copy link
Copy Markdown
Contributor

@SamuelMcAravey I've opened a new pull request, #131, to work on those changes. Once the pull request is ready, I'll request review from you.

…lidation, improve diagnostics (#131)

Co-authored-by: SamuelMcAravey <11021165+SamuelMcAravey@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 22, 2025 04:41
@SamuelMcAravey

Copy link
Copy Markdown
Member Author

@codex open a new pull request to apply changes based on the comments in this thread

@SamuelMcAravey

Copy link
Copy Markdown
Member Author

@copilot open a new pull request to apply changes based on the comments in this thread

Copilot AI commented Dec 22, 2025

Copy link
Copy Markdown
Contributor

@SamuelMcAravey I've opened a new pull request, #132, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 35 out of 35 changed files in this pull request and generated 13 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Bravellian.Platform.Modularity.Core/UiEngineResult.cs
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleRegistry.cs
Comment thread src/Bravellian.Platform.Modularity.Core/WebhookRequest.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineCapabilities.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineDiscoveryService.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineRegistry.cs
Comment thread src/Bravellian.Platform.Modularity.FullStack/UiEngineAdapter.cs
Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineRegistry.cs
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineRegistry.cs
@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

Testing

  • dotnet test tests/Bravellian.Platform.Tests/Bravellian.Platform.Tests.csproj --filter EngineRefactoringTests --logger "console;verbosity=minimal"

View task →

Copilot AI and others added 2 commits December 21, 2025 20:55
…132)

Co-authored-by: SamuelMcAravey <11021165+SamuelMcAravey@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 22, 2025 05:14
…on-for-transport-agnostic-modules-1ziys6' into codex/2025-12-21-refactor-solution-for-transport-agnostic-modules-1ziys6

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 35 out of 35 changed files in this pull request and generated 15 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineDiscoveryService.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.FullStack/UiEngineAdapter.cs
Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineCapabilities.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Core/WebhookRequest.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineRegistry.cs Outdated
Comment thread src/Bravellian.Platform.Modularity.Core/ModuleEngineRegistry.cs
Comment thread src/Bravellian.Platform.Modularity.Api/WebhookEngineAdapter.cs
Copilot AI review requested due to automatic review settings December 22, 2025 05:59
@SamuelMcAravey
SamuelMcAravey added this pull request to the merge queue Dec 22, 2025

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 38 out of 38 changed files in this pull request and generated 10 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +72 to +88
public object ResolveEngine(IModuleEngineDescriptor descriptor, IServiceProvider serviceProvider)
{
if (serviceProvider is null)
{
throw new ArgumentNullException(nameof(serviceProvider));
}

var instance = descriptor.Create(serviceProvider);

if (instance is null)
{
throw new InvalidOperationException(
$"The factory for module engine '{descriptor.ModuleKey}/{descriptor.Manifest.Id}' returned null.");
}

return instance;
}

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

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

Missing validation for the descriptor parameter. The overloaded ResolveEngine method validates serviceProvider for null but doesn't validate the descriptor parameter itself. If descriptor is null, the code will throw a NullReferenceException on line 79 when accessing descriptor.Create. Add a null check for the descriptor parameter with an appropriate ArgumentNullException.

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +151
foreach (var service in requiredServices)
{
if (string.IsNullOrWhiteSpace(service))
{
throw new InvalidOperationException(
$"Engine '{descriptor.ModuleKey}/{descriptor.Manifest.Id}' declares an empty required service identifier.");
}
}

var validator = services.GetService<IRequiredServiceValidator>();
if (validator is null)
{
throw new InvalidOperationException(
$"Engine '{descriptor.ModuleKey}/{descriptor.Manifest.Id}' declares required services but no {nameof(IRequiredServiceValidator)} is registered.");
}

var missing = validator.GetMissingServices(requiredServices.ToArray()) ?? Array.Empty<string>();
if (missing.Count > 0)
{
throw new InvalidOperationException(
$"Engine '{descriptor.ModuleKey}/{descriptor.Manifest.Id}' is missing required services: {string.Join(", ", missing)}.");
}
}

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

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

The ValidateRequiredServices method has code duplication with the UiEngineAdapter's ValidateRequiredServices method. Lines 129-150 in WebhookEngineAdapter are nearly identical to lines 78-99 in UiEngineAdapter. The only difference is the WebhookEngineAdapter has additional logic to aggregate required services from webhook metadata. Consider extracting the common validation logic into a shared helper method to improve maintainability and reduce duplication.

Copilot uses AI. Check for mistakes.
/// </summary>
internal static class ModuleEngineRegistry
{
private static readonly ConcurrentDictionary<string, List<IModuleEngineDescriptor>> Engines = new(StringComparer.OrdinalIgnoreCase);

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

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

The ConcurrentDictionary is being used with an external lock, which negates the benefits of using a concurrent collection. Since all access to the Engines dictionary is protected by RegistryLock, consider using a regular Dictionary instead. ConcurrentDictionary adds overhead that isn't needed when external synchronization is already in place.

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +89
private static void ValidateWebhookMetadataUniqueness(IModuleEngineDescriptor descriptor)
{
var metadata = descriptor.Manifest.WebhookMetadata;
if (metadata is null)
{
return;
}

var seen = new HashSet<(string Provider, string EventType)>(new WebhookMetadataKeyComparer());

foreach (var entry in metadata)
{
if (!seen.Add((entry.Provider, entry.EventType)))
{
throw new InvalidOperationException(
$"Engine '{descriptor.ModuleKey}/{descriptor.Manifest.Id}' declares duplicate webhook metadata for provider '{entry.Provider}' and event '{entry.EventType}'.");
}

foreach (var existingList in Engines.Values)
{
foreach (var existing in existingList)
{
if (existing.Manifest.WebhookMetadata is null)
{
continue;
}

foreach (var existingEntry in existing.Manifest.WebhookMetadata)
{
if (string.Equals(existingEntry.Provider, entry.Provider, StringComparison.OrdinalIgnoreCase)
&& string.Equals(existingEntry.EventType, entry.EventType, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
$"Webhook provider '{entry.Provider}' and event '{entry.EventType}' are already handled by engine '{existing.ModuleKey}/{existing.Manifest.Id}'.");
}
}
}
}
}

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

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

The validation logic iterates through Engines.Values while inside a lock that's meant to protect Engines. However, ValidateWebhookMetadataUniqueness is called within the Register method before the new descriptor is added to the list. This means the cross-engine validation checks existing engines but not the newly registered ones in the same batch. If multiple descriptors with the same webhook metadata are passed in the same descriptors enumerable, only the first will be caught by the local HashSet check, but duplicates later in the same batch won't be detected until they're individually validated.

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +68
public TContract ResolveEngine<TContract>(ModuleEngineDescriptor<TContract> descriptor, IServiceProvider serviceProvider)
where TContract : notnull
{
if (serviceProvider is null)
{
throw new ArgumentNullException(nameof(serviceProvider));
}

var instance = descriptor.Factory(serviceProvider);

if (instance is null)
{
throw new System.InvalidOperationException(
$"The factory for module engine '{descriptor.ModuleKey}/{descriptor.Manifest.Id}' returned null.");
}

return instance;
}

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

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

Missing validation for the descriptor parameter. The method validates serviceProvider for null but doesn't validate the descriptor parameter itself. If descriptor is null, the code will throw a NullReferenceException on line 59 when accessing descriptor.Factory. Add a null check for the descriptor parameter with an appropriate ArgumentNullException.

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +136
foreach (var service in requiredServices)
{
if (string.IsNullOrWhiteSpace(service))
{
throw new InvalidOperationException(
$"Engine '{descriptor.ModuleKey}/{descriptor.Manifest.Id}' declares an empty required service identifier.");
}
}

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

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

This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.

Copilot uses AI. Check for mistakes.
Comment on lines +145 to +152
foreach (var meta in metadataCollection)
{
if (string.Equals(meta.Provider, provider, StringComparison.OrdinalIgnoreCase)
&& string.Equals(meta.EventType, eventType, StringComparison.OrdinalIgnoreCase))
{
return descriptor;
}
}

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

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

This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.

Copilot uses AI. Check for mistakes.
Comment on lines +170 to +176
foreach (var descriptor in list)
{
if (string.Equals(descriptor.Manifest.Id, engineId, StringComparison.OrdinalIgnoreCase))
{
return descriptor;
}
}

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

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

This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +85
foreach (var service in requiredServices)
{
if (string.IsNullOrWhiteSpace(service))
{
throw new InvalidOperationException(
$"Engine '{descriptor.ModuleKey}/{descriptor.Manifest.Id}' declares an empty required service identifier.");
}
}

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

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

This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.

Copilot uses AI. Check for mistakes.
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Dec 22, 2025
@SamuelMcAravey
SamuelMcAravey merged commit 92ecb0f into main Dec 22, 2025
7 checks passed
@SamuelMcAravey
SamuelMcAravey deleted the codex/2025-12-21-refactor-solution-for-transport-agnostic-modules-1ziys6 branch December 22, 2025 06:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants