Fix tractable findings from PR #678 multi-model review#686
Open
eanzhao wants to merge 4 commits into
Open
Conversation
A post-merge multi-model review of PR #678 (merged into dev as 601d92b) surfaced ~18 distinct findings. This addresses the three that are safely fixable without architectural rework: - StreamingToolExecutor: the channel coordinator was started fire-and-forget, so a fault inside the loop was unobserved — the loop silently died and GetRemainingResultsAsync hung forever. Retain the coordinator task, race state requests against it so faults surface to the caller, and observe the task on Dispose. Adds regression test CoordinatorFault_ShouldSurfaceToCaller_NotHang. - codex-refactor-loop skill prompts: replace hardcoded /Users/auric/aevatar/ paths in remote-ci-fix.md and test-add.md with $REPO_ROOT/, matching the convention already used by audit.md / implement.md / verify.md. - Directory.Packages.props: correct the OpenTelemetry comment that claimed the shared variable holds 1.15.3 when it actually holds 1.15.1. Remaining findings (IProjectionSessionEventCodec<AGUIEvent> DI collision, UserAgentCatalogProjector StateVersion synthesis, etc.) are documented in the PR description; they need dedicated design work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## dev #686 +/- ##
==========================================
+ Coverage 82.42% 82.44% +0.01%
==========================================
Files 938 939 +1
Lines 59753 59788 +35
Branches 7831 7830 -1
==========================================
+ Hits 49251 49290 +39
+ Misses 7128 7125 -3
+ Partials 3374 3373 -1
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 9 files with indirect coverage changes 🚀 New features to boost your workflow:
|
…view) M4 — IProjectionSessionEventCodec<AGUIEvent> DI collision: NyxId chat, GAgent draft-run and script-service AGUI all projected AGUIEvent through one shared IProjectionSessionEventHub<AGUIEvent>; TryAddSingleton silently dropped whichever module's codec registered second, so a pipeline could run on the wrong codec/channel. Each pipeline now builds its own channel-scoped hub via a factory registration; adds a dedicated ScriptServiceAguiSessionEventCodec (it previously rode on the draft-run codec through the shared registration). M5 — UserAgentCatalogProjector StateVersion: the projector merges two authoritative sources (catalog + runner) and synthesized StateVersion as their sum. Add per-source monotonic guards so a stale/replayed event for one source cannot roll its version back or re-write older fields over newer ones; with the guards the sum is strictly monotonic per document and a valid overwrite watermark. The honest per-source versions are kept; a vector-typed StateVersion would need a proto schema change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three *ProjectionInfrastructureTests asserted `ImplementationType == typeof(<Projector>)` for projection materializers. AddProjectionArtifactMaterializer / AddCurrentStateProjectionMaterializer register materializers wrapped in the Observed*Materializer<TContext, TProjector> observability decorator, so ImplementationType is the decorator type — not the bare projector. These tests had been red on dev independently of PR #678 and of this branch's fixes. Assert the decorator wraps the expected projector via ImplementationType.GenericTypeArguments instead (the Observed* decorators are internal to Aevatar.CQRS.Projection.Core and cannot be named from this test assembly). 10 assertions across 3 files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
PR #678 (Refactor iter1: clean 3 architectural violations + add codex-refactor-loop skill)
was merged into
dev(merge commit601d92b2) before a post-merge review completed.This follow-up PR carries that review as its body and fixes every tractable finding.
The review ran 6 models over the #678 diff via
/opencode-pr-review.deepseek-v4-pro · glm-5.1 · mimo-v2.5-pro · codex produced parseable reviews;
kimi timed out and gemini OOM'd — both excluded. 23 raw clusters were
consolidated into ~18 distinct findings; each is dispositioned below.
IProjectionSessionEventCodec<AGUIEvent>DI collisionStateVersionDeltaContent✅ Fixed
M4 —
IProjectionSessionEventCodec<AGUIEvent>DI collision · major (codex) — PR-introducedGAgentService.ProjectionregisteredGAgentDraftRunSessionEventCodecand NyxidChatregistered
NyxIdChatSessionEventCodec— both asIProjectionSessionEventCodec<AGUIEvent>via
TryAddSingleton, so the second registration was a silent no-op and a pipelinecould run on the wrong codec/channel. Script-service AGUI had no codec at all and rode
on the draft-run one. Fix: each pipeline (NyxId chat, draft-run, script-service)
now builds its own channel-scoped
ProjectionSessionEventHub<AGUIEvent>via a factoryregistration; adds a dedicated
ScriptServiceAguiSessionEventCodec. The hub holds nomutable state, so a per-consumer instance is safe.
M5 —
UserAgentCatalogProjectorsynthesizesStateVersion· major (codex)The projector merges two authoritative sources (catalog + runner) and set
StateVersion = CatalogSourceVersion + RunnerSourceVersion. It had no per-sourcestaleness guard, so a stale/replayed event for one source would roll that source's
version back and re-write older fields. Fix: added per-source monotonic guards —
a catalog/runner commit not newer than the document's stored version for that source
is skipped. With the guards every accepted event strictly advances one source, so the
sum is strictly monotonic per document and a valid overwrite watermark. The honest
per-source versions are retained; a fully vector-typed
StateVersionwould need aproto schema change (noted for a dedicated change).
M6 —
StreamingToolExecutorcoordinator started fire-and-forget · major (v4-pro + mimo, consensus)_ = RunCoordinatorAsync()— a fault in the loop was unobserved, the loop silentlydied, and
GetRemainingResultsAsynchung forever. Fix: the coordinator task isretained;
RequestStateAsyncraces the request against it so faults surface to thecaller;
Disposeobserves a faulted task. Regression testCoordinatorFault_ShouldSurfaceToCaller_NotHangadded.M1 — hardcoded
/Users/auric/aevatar/paths in codex-refactor-loop prompts · major (glm-5.1)remote-ci-fix.md/test-add.md→$REPO_ROOT/, matchingaudit.md/implement.md/verify.md.m1 — misleading OpenTelemetry version comment · minor (glm-5.1 + mimo)
Directory.Packages.propscomment claimed the variable holds1.15.3; it holds1.15.1.Comment corrected — security-critical packages are individually pinned to
1.15.3.✔️ Verified — no fix needed
HouseholdEntityToolSourceis registered by-type, so DI auto-suppliesthe new
IActorDispatchPortctor param; tests construct it directly. No change needed.📝 Reviewed — current code is correct (no change, with reasons)
HouseholdEntityreasoning aggregateschunk.DeltaContent. For a normalmodel this reconstructs exactly the assistant answer the old
ChatAsyncreturned; thedecision text (
NO_ACTION) is content, not reasoning. Folding inDeltaReasoningContentwould pollute the decision with chain-of-thought — a regression. Correct as-is.
StreamingToolExecutor:326hook-rewrite-to-destructive "records error +continues" — this is pre-existing behavior (the old code set
_hasErroredandcontinued too); the refactor only changed the mechanism. Not a Refactor iter1: clean 3 architectural violations + add codex-refactor-loop skill #678 regression.
ScopeServiceEndpoints2-min linked timeout now coversRunRuntimeAsync.Dispatch is accepted-only (fast); the timeout also usefully bounds a stuck mailbox.
v4-pro's suggested split would leave dispatch unbounded — current code is acceptable.
📝 Minor findings — reviewed
NyxIdChatStreamingRunner:48finally-block OCE — the exception falls through to theouter cancellation handler; behavior is correct.
ScopeBindingCommandApplicationService"hardcodedPropagationStage" (mimo) — the namedfields (
AcceptanceStage/PropagationStage) do not exist in the code; inaccurate finding.StreamingToolExecutor:155GetCompletedResultsrace — reviewer notes it self-resolves.StreamingToolExecutor:198Disposerace — mitigated by the M6 fix (RequestStateAsyncnow throws instead of hanging).
ConnectedServiceSpecCachedual constructor — deliberate & documented (// Fix (remote-ci...): DIValidateServicector-ambiguity workaround). Left intact by design.NyxIdSpecCatalogsingleton disposal — already guarded (Interlocked, OCE catches,finallycleanup); the fire-and-forget cleanup is acceptable for a shutdown singleton.UserAgentCatalogProjectorrunner-id fallback — the runner branch only runs after asuccessful
TryUnpackState<SkillRunnerState>; thePublisherActorIdfallback is sound.architecture_guards.shtools/scan — currently passes; the concern is hypothetical.🚫 Out of scope — already acknowledged by PR #678
ChatQueryEndpointsexposes 6 unauthenticated/actors/{actorId}endpoints. PR Refactor iter1: clean 3 architectural violations + add codex-refactor-loop skill #678's own "Out-of-scope (security follow-up needed)" section already
flags this for a
/cso/ threat-model pass; the real fix is a caller-scope contract.Test plan
dotnet build— affected projects buildAevatar.GAgents.ChannelRuntime.Tests— 820/820 pass (M5)Aevatar.AI.Tests— 556/556 pass (M4 NyxId chat, M6)Aevatar.GAgentService.Tests— M4/M5 add 0 new failurestools/ci/architecture_guards.sh— all guards passtools/ci/test_stability_guards.sh— passesdevbase🤖 Generated with Claude Code