iter47 issue-877 chat-endpoints-own-lifecycle-and-compensation: typed facade + actor-owned compensation#885
Conversation
… lifecycle facades + actor 拥有 compensation/terminal-state 按 issue #877 Phase 9 r1 consensus(meta-judge: structural,no new actor): 1. NyxIdChatLifecycleFacade.cs(新) + StreamingProxyChatLifecycleFacade.cs(新):typed lifecycle command facade 2. NyxIdChatEndpoints: 删 IActorRuntime / IRegistryCommandPort / IChatHistoryStore 注入,改 facade only 3. StreamingProxyEndpoints: 进一步收窄(与 #874 不冲突) 4. NyxIdChatGAgent / StreamingProxyGAgent: 扩展 lifecycle + compensation events(actor-owned) 5. agent_run.proto: typed lifecycle event 字段 6. ServiceCollectionExtensions: 注册 facade + 删 store injection 7. 测试覆盖 endpoint adapter only + facade dispatch + actor compensation events 不引入新 actor / 不 saga actor。 Closes #877 ⟦AI:AUTO-LOOP⟧
🤖 Phase 8 quality r1 approveTL;DR
详细说明
注入删除也对齐本轮目标: NyxID create/delete 改为注入 LOC 方面,+1249/-436 可接受: 两个新 facade 文件约 611 LOC,测试约 473 LOC,endpoint 文件净删约 362 LOC。增长主要来自 typed receipt/status、actor-owned compensation payload、以及回归测试,不是平行第二套业务链路。 验证已跑:
Marker: 📎 完整 codex 原始输出(存档备查)---
pr: 885
role: quality
round: r1
head: 38a85de1cb1b1f69a6233a2c1514c7c0c1a1d8e1
verdict: approve
---
## Verdict
Approve: the typed lifecycle facade extraction is readable and focused; the name is clear enough for the current boundary; the removed endpoint store/runtime injections are accounted for; and the 1249 LOC increase is justified by moving lifecycle orchestration into typed services plus targeted tests.
## Evidence
- `agents/Aevatar.GAgents.NyxidChat/NyxIdChatLifecycleFacade.cs`: `NyxIdChatLifecycleFacade` clearly names the boundary as NyxID chat conversation lifecycle. It owns create/delete, route resolution, registration visibility handling, and actor-owned compensation dispatch without adding a generic manager/helper abstraction.
- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyChatLifecycleFacade.cs`: `StreamingProxyChatLifecycleFacade` is verbose but accurate: it groups chat lifecycle actions that were formerly scattered through HTTP handlers, including run chat, join, delete room, participant listing, subscription attach/detach, and terminal-state publication.
- Endpoint injection cleanup is consistent in the PR diff: create/delete in `NyxIdChatEndpoints.cs` now inject `NyxIdChatLifecycleFacade`; StreamingProxy chat/join/delete/list/subscription paths now inject `StreamingProxyChatLifecycleFacade` instead of the previous runtime, store, interaction, participant, and subscription dependencies.
- Remaining direct `IActorRuntime` usage in NyxID stream/approve/relay endpoints is outside this PR's targeted create/delete lifecycle extraction and predates this diff; it is not an omitted removal from the changed lifecycle paths.
- DI registration is minimal: `AddNyxIdChat()` and `AddStreamingProxy()` each add one singleton facade beside existing module services. No second registration surface or service-locator fallback was introduced for the facades.
- LOC shape is reasonable for the typed facade pattern: the two new facade files add 611 LOC, test updates add 473 LOC, and endpoint files remove 362 LOC while retaining HTTP/SSE mapping. The growth is mostly explicit typed receipts/statuses and regression coverage rather than parallel business logic.
- The new proto messages for NyxID compensation are strongly typed command payloads, matching the consensus that chat actors own compensation/terminal-state instead of endpoints doing rollback inline.
## Verification
- `git diff --check origin/auto-refact-dev...origin/refactor/iter47-cluster-002-chat-endpoints-own-lifecycle-and-compensation` passed.
- `bash tools/ci/test_stability_guards.sh` passed in detached PR worktree.
- `dotnet restore test/Aevatar.AI.Tests/Aevatar.AI.Tests.csproj --nologo` completed with existing package/analyzer warnings.
- `dotnet test test/Aevatar.AI.Tests/Aevatar.AI.Tests.csproj --nologo --no-restore --filter "FullyQualifiedName~NyxIdChatEndpointsCoverageTests|FullyQualifiedName~StreamingProxyCoverageTests|FullyQualifiedName~StreamingProxyEndpointsCoverageTests"` passed: 121 passed, 0 failed, 0 skipped. Existing analyzer/obsolete warnings were present outside this PR's quality surface.
REVIEW_DONE:885:quality:approve
⟦AI:AUTO-LOOP⟧⟦AI:AUTO-LOOP⟧ |
PR #885 Tests Review r1Role: Phase 8 Reviewer (tests) Verdictreject Scope Checked
Blocking FindingNyxID actor-owned compensation is not actually covered by tests. The new production code moves compensation into Relevant code:
Required fix:
Non-Blocking Notes
Verification
REVIEW_DONE:885:tests:reject ⟦AI:AUTO-LOOP⟧ |
Phase 8 Reviewer -
|
📊 状态 — r1 architect 2 substantive reject(facade 仍非 typed command + actor 未真持 lifecycle)→ 派 fix-r2
🤖 controller status banner ⟦AI:AUTO-LOOP⟧ |
🤖 fix-codex round 2 — typed command facades + actor-owned lifecycle eventsApplied 2/2 architect-required fixes; rejected 0; blocked 0.
Validation:
Report: FIX_DONE:885:round-2:applied-2:rejected-0:blocked-0 |
…n 完整化 3 reviewer r1: architect/tests reject、quality approve。r2 应用 2 处: - NyxIdChat / StreamingProxy chat lifecycle facade 与 actor compensation 全链路对齐 - 测试覆盖 facade dispatch / actor compensation / terminal-state readmodel ⟦AI:AUTO-LOOP⟧
📊 状态 — fix-r2 push + reviewer r2 dispatchfix-r2 commit 派 3 reviewer r2 验证 r1 reject(architect/tests)是否真解决。3/3 approve + CI green → merge。 ⟦AI:AUTO-LOOP⟧ |
🤖 Phase 8 Reviewer —
|
📊 codex 进展 review-pr885-quality-r2 (⏳ 进行中; 已跑 8 min)
|
📊 codex 进展 review-pr885-tests-r2 (⏳ 进行中; 已跑 9 min)
|
📊 状态 — r2 全 reject → fix-r3 dispatch3/3 r2 reviewer reject。主要 blocking:
派 fix-r3。max_fix_rounds=6,仍在范围。 ⟦AI:AUTO-LOOP⟧ |
…ission gate + room unregister + real-actor compensation tests 1. ARCHITECT: NyxId create resolver 显式 set CreatedLocally(ForwardToGagent=false / fresh=true);actor 不再从 id prefix 推断 → 既有路由 actor 不会被误销毁 2. QUALITY-1: StreamingProxy room delete 补回 IGAgentActorRegistryCommandPort 注销(facade 漏抄);delete-side fail 时返回失败 3. QUALITY-2: NyxId delete resolver non-accepted admission 时拒绝解析 → 403/404 路径不再触发 actor unregister/delete history 4. TESTS: NyxIdChatGAgentTests 加真 envelope test 覆盖 HandleCreationCompensationAsync(createdLocally=true|false 双 branch)+ HandleDeletionCompensationAsync(registration restore);endpoint helper 不再 short-circuit 修后 lifecycle 127 passed + test_stability_guards passed。 ⟦AI:AUTO-LOOP⟧
📊 状态 — fix-r3 push + reviewer r3 dispatchfix-r3 commit
127 passed + test_stability_guards passed。派 3 reviewer r3。 ⟦AI:AUTO-LOOP⟧ |
📊 状态 — r4 3/3 reject(深层架构 BLOCKER)→ fix-r5 dispatchr4 verdict:全 reject(architect/tests/quality)。3 BLOCKER:
派 fix-r5 with explicit guidance(推荐 C: committed event + readmodel observe,符合 CLAUDE.md)。 ⟦AI:AUTO-LOOP⟧ |
📊 codex 进展 review-pr885-architect-r4 (⏳ 进行中; 已跑 8 min)
|
📊 codex 进展 review-pr885-quality-r4 (⏳ 进行中; 已跑 8 min)
|
📊 codex 进展 review-pr885-tests-r4 (⏳ 进行中; 已跑 8 min)
|
…+ real DELETE pipeline architect r4 3 个 BLOCKER: 1. InMemoryDict 违 actor/distributed 规则 → 改 StreamActorOutcomeChannel(Orleans IStreamProvider) 2. dispatch-before-outcome 顺序倒置 → subscribe-before-dispatch + actor failure 只发 outcome 不 rethrow 3. DELETE endpoint 测试仍 inline fake → 删,走真 dispatch pipeline 修法: - 删 InMemoryActorOutcomeChannel.cs - 新 StreamActorOutcomeChannel.cs(IStreamProvider 后端,multi-silo OK) - DefaultCommandOutcomeDispatchService subscribe 先,然后 dispatch,await outcome(支持 dispatch 异常 fallthrough) - 约束 outcome dispatch 只接受 protobuf 消息 - NyxIdChatGAgent registration failure 时发 unavailable outcome 后**不 rethrow**(endpoint 基于 outcome map 503) - 删 DELETE endpoint inline fake dispatch helper;tests 走真 DefaultCommandDispatchService → ActorCommandTargetDispatcher → NyxIdChatGAgent.HandleDeleteConversationAsync - 加 endpoint 503 assertion(真 outcome 路径) 41/41 + 72/72 PASS,test_stability_guards PASS。 ⟦AI:AUTO-LOOP⟧
📊 状态 — fix-r5 push,派 r5 reviewer × 3fix-r5 commit
11 files +127/-111(净删除)。41/41 + 72/72 PASS。 派 3 reviewer r5。fix_round=5/6,若 r5 仍 reject → reflector 评估。 ⟦AI:AUTO-LOOP⟧ |
…endpoints-own-lifecycle-and-compensation
✅ Phase 8 Reviewer r5 3/3 APPROVE — 深层架构 fix 验证通过
r4 architect 3 BLOCKER 全 resolved(StreamActorOutcomeChannel + subscribe-before-dispatch + real DELETE pipeline)。本 PR 是 meta-pattern issue #891 第一个深度落地实现。 CI 状态已 update-branch(picks up #890 fix);CI 重跑中。CI green → merge,Closes #877。 ⟦AI:AUTO-LOOP⟧ |
📊 codex 进展 review-pr885-architect-r5 (⏳ 进行中; 已跑 8 min)
|
📊 codex 进展 review-pr885-quality-r5 (⏳ 进行中; 已跑 8 min)
|
📊 codex 进展 review-pr885-tests-r5 (⏳ 进行中; 已跑 8 min)
|
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## auto-refact-dev #885 +/- ##
===================================================
+ Coverage 82.79% 82.81% +0.02%
===================================================
Files 991 995 +4
Lines 62523 62613 +90
Branches 8099 8107 +8
===================================================
+ Hits 51763 51852 +89
+ Misses 7256 7255 -1
- Partials 3504 3506 +2
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 5 files with indirect coverage changes 🚀 New features to boost your workflow:
|
…实源,删 singleton + dual store + readmodel design consensus(Phase 9 r1 unanimous,#887):room-actor-authority-delete-singleton。 删除: - StreamingProxyParticipantGAgent(singleton cluster-scoped actor) - streaming_proxy_participant_messages.proto + State - Projection/projector + studio_projection_readmodels.proto 内 StreamingProxyParticipantCurrentStateDocument - projection readmodel registration + cluster-scoped 启动注册 - ActorBackedStreamingProxyParticipantStore(dual-write surface)+ 应用层 IActorRuntime 注入 保留/重做: - StreamingProxyGAgent per room 作 participant authority(已 own participants 字段) - Endpoint 改 typed application/interaction service 注入,移走 IActorRuntime - 应用层调 room current-state query 替代旧 store - 不动 StreamingProxyNyxParticipantCoordinator(facade 边界,#885 并行) - 不动 NyxID LLM provider / room chat semantics - 不动 chat lifecycle path(那是 #885/#877) 测试:room actor authority + endpoint typed service + 删 singleton 后无引用残留 + projection 物化路径。 31 files +399/-1235(净 -836 LOC,真删除而非空壳)。 ⟦AI:AUTO-LOOP⟧
… + dual store,room actor 单事实源 (#893) * iter50 issue-887 streaming-proxy-participant-authority: room actor 单事实源,删 singleton + dual store + readmodel design consensus(Phase 9 r1 unanimous,#887):room-actor-authority-delete-singleton。 删除: - StreamingProxyParticipantGAgent(singleton cluster-scoped actor) - streaming_proxy_participant_messages.proto + State - Projection/projector + studio_projection_readmodels.proto 内 StreamingProxyParticipantCurrentStateDocument - projection readmodel registration + cluster-scoped 启动注册 - ActorBackedStreamingProxyParticipantStore(dual-write surface)+ 应用层 IActorRuntime 注入 保留/重做: - StreamingProxyGAgent per room 作 participant authority(已 own participants 字段) - Endpoint 改 typed application/interaction service 注入,移走 IActorRuntime - 应用层调 room current-state query 替代旧 store - 不动 StreamingProxyNyxParticipantCoordinator(facade 边界,#885 并行) - 不动 NyxID LLM provider / room chat semantics - 不动 chat lifecycle path(那是 #885/#877) 测试:room actor authority + endpoint typed service + 删 singleton 后无引用残留 + projection 物化路径。 31 files +399/-1235(净 -836 LOC,真删除而非空壳)。 ⟦AI:AUTO-LOOP⟧ * fix(pr893 r2): r1 architect+quality 2 BLOCKER — projection activation + actor-owned idempotent join quality+architect r1 2 BLOCKER: 1. Participant readmodel 无 projection activation path → 加 StreamingProxyCommittedStateProjectionActivationPlanProvider + ServiceCollectionExtensions DI 2. Cold/stale projection 漏入 write control flow → 删 service-layer dedupe(read projection 决定 dispatch);dedupe 决策上移 actor 内(StreamingProxyGAgent.HandleGroupChatParticipantJoined 见 participants.Contains(id) → 不 publish event) 更新 stale test: - StreamingProxyCoverageTests:duplicate same-id joins 期望 publish count 1(不再 2),actor-owned idempotent ⟦AI:AUTO-LOOP⟧ * test(pr893 r3): 加 StreamingProxyCommittedStateProjectionActivationPlanProviderTests + DI 注册 + 物化链 omnibus r2 mixed:架构 OK 但缺 activation provider 回归测试。 加 8 tests covering: - Provider 真给出 activation plan - Plan 真覆盖 room state-event types - DI 注册 provider 到 dispatcher chain - committed state-event → projection runs → readmodel populated ⟦AI:AUTO-LOOP⟧
摘要
iter47 issue-877 cluster-002-chat-endpoints-own-lifecycle-and-compensation(high)。
NyxIdChatLifecycleFacade+StreamingProxyChatLifecycleFacade(typed facade)承担 lifecycle;现有 actor(NyxIdChatGAgent / StreamingProxyGAgent)拥有 compensation/terminal-state(无新 actor)。Phase 9 共识
META_JUDGE_DONE:consensus:structural:use typed chat command facades with existing chat actors owning lifecycle compensation terminal state and endpoints adapter-only改动范围
12 files (+1249/-436):
不引入新 actor / 不 saga actor。
Closes #877
📢 cc: @loning @eanzhao
🤖 Auto-loop / codex-refactor-loop iter47
⟦AI:AUTO-LOOP⟧