Skip to content

iter104 cluster-1 #1063: 删 StreamingProxyChatLifecycleFacade,fold 进 StreamingProxyGAgent#1069

Merged
loning merged 2 commits into
auto-refact-devfrom
iter104/cluster-1-streamingproxy-delete-facade
May 26, 2026
Merged

iter104 cluster-1 #1063: 删 StreamingProxyChatLifecycleFacade,fold 进 StreamingProxyGAgent#1069
loning merged 2 commits into
auto-refact-devfrom
iter104/cluster-1-streamingproxy-delete-facade

Conversation

@loning
Copy link
Copy Markdown
Contributor

@loning loning commented May 26, 2026

Summary

Phase 9 r2 consensus(reflector r1 retry-fix narrowing → 3/3 unanimous):

  • 新 actor type;reuse existing StreamingProxyGAgent

  • delete/fold StreamingProxyChatLifecycleFacade orchestration into actor handlers

  • 保留 deprecated compat endpoints

  • reuse LlmSessionGAgent(与 [iter103 cluster-1] Responses/Messages 在 Application 层直接跑完整 LLM 会话(违反 actor-owned business progression) #1058 耦合)

  • Old:StreamingProxyChatLifecycleFacade(Application 层)持 chat continuation lifecycle orchestration → 违反业务推进内聚

  • New:StreamingProxyGAgent typed self continuation StreamingProxyChatLifecycleContinuationRequested 推进 Nyx participant join/reply/terminal effects;Endpoints 只做 HTTP normalize+dispatch

违反 CLAUDE.md:

  • 「Actor 设计 / 生命周期 / 执行模型」「业务推进内聚」
  • 「命令骨架内聚:Normalize → Resolve → Build → Dispatch → Receipt → Observe」

Scope

  • 6 files changed (+180 / -399)
  • 删 StreamingProxyChatLifecycleFacade + DI 注册
  • StreamingProxyEndpoints 保留 deprecated compat surface
  • StreamingProxyGAgent 加 typed self continuation message
  • streaming_proxy_messages.proto 加新 typed event
  • StreamingProxyCoverageTests 更新

Verification

  • dotnet build aevatar.slnx --nologo:0 errors
  • AI tests (StreamingProxy filter):passed
  • Workflow Host API tests:passed
  • Workflow SDK tests:passed
  • architecture/test_stability guards:passed

closes #1063

🤖 Auto-loop / codex-refactor-loop iter104

⟦AI:AUTO-LOOP⟧

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

📊 PR open + 3 reviewers 派出(Phase 8 r1)

维度
阶段 👀 phase:reviewing(r1)
Reviewers architect / tests / quality
来源 Phase 9 r2 consensus(#1063 closes after merge)
Diff size 6 files (+180 / -399)
是否需要人介入 ❌ 否

⟦AI:AUTO-LOOP⟧

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

🤖 Phase 8 Reviewer — quality (round 1)

TL;DR


详细说明

整体质量可接受:StreamingProxyChatLifecycleFacade 删除后,DI 注册和 endpoint 注入已清掉;StreamingProxyChatLifecycleContinuationRequested 这个名称准确表达了“chat lifecycle 的下一拍 self continuation 请求”,和 handler/proto 对齐;新增 actor handler 约 35 行,流程直接,没有新增单实现抽象或明显死码。

我保留一个轻量 comment:agents/Aevatar.GAgents.StreamingProxy/StreamingProxyEndpoints.cs:241agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:42agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:89 的 iter104 自文档把 Old patternNew principle 压成同一个很长的单行。意思是完整的,但本轮 prompt 明确要求 // Refactor (iterN/cluster-XXX): 的 Old/New block 对非审计读者清楚;建议改成仓库已有的三行 block 格式即可。


📎 完整 codex 原始输出(存档备查)
---
pr: 1069
role: quality
verdict: comment
---

## Verdict
代码质量整体可接受,facade 删除后的调用面清理完整;唯一质量意见是 iter104 自文档注释虽然表达了 Old/New 意图,但三处都压成了过长单行,不符合本轮要求的 Old/New block 可读性。

## Evidence
- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyEndpoints.cs:241`, `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:42`, `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:89`: `// Refactor (iter104/cluster-1): ... Old pattern ... New principle ...` 内容能说明旧 facade owned continuation、新 actor typed continuation owned lifecycle,但它被压成单行并重复三次;和同文件既有 `Refactor` 注释的多行 `Old pattern` / `New principle` block 风格不一致,非审计读者扫读时可读性较差。
- Dead code check: `StreamingProxyChatLifecycleFacade``StreamingProxyChatLifecycleRequest``StreamingProxyJoinLifecycle*``StreamingProxyRoomDeleteLifecycle*``StreamingProxySubscriptionLifecycle*` 仅剩自文档文本和测试中的 negative assertion,没有生产调用残留。
- Naming check: `StreamingProxyChatLifecycleContinuationRequested` 清楚表达了 self continuation 的业务意图;handler `HandleChatLifecycleContinuationRequested` 与 proto message、endpoint name `continueChatLifecycle` 一致。
- Complexity/readability check: 新增 actor handler约 35 行,流程为 token guard -> ensure participants -> generate replies -> terminal state,分支少且局部变量命名直接;endpoint 删除 facade 后未引入新抽象。

## What would change your verdict
把三处 iter104 自文档改成现有仓库风格的多行 block,例如:

```csharp
// Refactor (iter104/cluster-1):
//   Old pattern: StreamingProxyChatLifecycleFacade owned chat continuation orchestration in Application layer.
//   New principle: StreamingProxyGAgent typed continuation owns lifecycle; deprecated compat endpoints only normalize and dispatch typed commands.

REVIEW_DONE:1069:quality-r1:comment


</details>

REVIEW_DONE:1069:quality-r1:comment
⟦AI:AUTO-LOOP⟧

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

🤖 Phase 8 Reviewer — architect (round 1)

TL;DR

  • 中文:结论是 reject;facade 已删除,但新 self continuation 把 Nyx/LLM streaming I/O 放进了 actor handler。
  • EN: reject; the facade is gone, but the new self continuation now runs Nyx/LLM streaming I/O inside the actor handler.
  • 需要处理:把外部 streaming/participant runner 放回 actor turn 之外,只把结果作为 typed command/event 回到 StreamingProxyGAgent

详细说明

架构主问题在 agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:86 新增的 HandleChatLifecycleContinuationRequested。这个 handler 是 StreamingProxyGAgent 的 actor event handler;但在同一个 actor turn 里,它从 DI 解析 StreamingProxyNyxParticipantCoordinator,再 await EnsureParticipantsJoinedAsyncGenerateRepliesAsync。后者会做 Nyx/LLM streaming I/O,并通过 room command service 发 join/reply/leave/terminal 相关请求。

这和仓库规则冲突:actor turn 应该只推进 actor-owned 事实;跨 actor/外部工作需要 continuation 化,不能在当前 turn 内等待外部 reply 或 streaming。文件本身现有注释也写明 External Nyx streaming I/O stays outside actor turns.。本 PR 的 typed self continuation 方向是清楚的,但 continuation handler 的职责边界需要再收窄:actor 可以发布/持久化 typed continuation request,外部 adapter/runner 消费它;adapter 完成每个 participant join/reply/terminal 后,再以 typed room command/event 回到 StreamingProxyGAgent 由 actor 提交事实。

正向检查结果:StreamingProxyChatLifecycleFacade 的 live type 和 DI 注册已删除,残留只在注释/测试断言里;未引入新 actor type;未触动 LlmSessionGAgent;新增 proto message 使用新字段号,未发现字段复用/重编号。


📎 完整 codex 原始输出(存档备查)
---
pr: 1069
role: architect
verdict: reject
---

## Verdict
reject — the facade was deleted, but the replacement moves Nyx participant streaming I/O and room command dispatch into `StreamingProxyGAgent` actor handling, which violates the actor execution boundary.

## Evidence
- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:86` adds `HandleChatLifecycleContinuationRequested` as an actor event handler for the typed self-continuation. Inside that same actor turn, `StreamingProxyGAgent.cs:95-112` resolves `StreamingProxyNyxParticipantCoordinator`, awaits participant joins, and awaits `GenerateRepliesAsync`. That coordinator performs external Nyx/LLM streaming I/O and forwards room commands (`StreamingProxyNyxParticipantCoordinator.cs:95-153`, `StreamingProxyNyxParticipantCoordinator.cs:68-73`). This newly introduced path conflicts with CLAUDE.md: `业务推进内聚:工作流推进(成功/失败/分支/重试)在 Actor 事件处理流程内完成,保证顺序性与可重放性。`, `跨 actor 等待 continuation 化:"发送请求 → 结束当前 turn → reply/timeout event 唤醒继续";禁止当前 turn 同步等待或通过侧读/伪 RPC 绕过。`, and the existing type-level contract in `StreamingProxyGAgent.cs:22`: `External Nyx streaming I/O stays outside actor turns.` The self-continuation should trigger adapter work outside the actor turn or split each external result back into typed actor messages.
- Positive checks: the live `StreamingProxyChatLifecycleFacade` type and DI registration are removed; remaining references are comments/tests only. No new actor type is introduced. `LlmSessionGAgent` is not touched. The new continuation payload is strongly typed in `streaming_proxy_messages.proto` with new field numbers and no removed/renumbered proto fields.

## What would change your verdict
Keep `StreamingProxyGAgent` as the authority for committed room facts, but do not run Nyx/LLM streaming or same-room command-service orchestration inside the actor handler. A compliant shape would have the actor persist/publish a typed continuation request, an adapter/runner consume that request outside the actor turn, and each participant join/reply/terminal result return as typed room commands/events that the actor commits.

REVIEW_DONE:1069:architect-r1:reject
⟦AI:AUTO-LOOP

REVIEW_DONE:1069:architect-r1:reject
⟦AI:AUTO-LOOP⟧

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

🤖 Phase 8 Reviewer — tests (round 1)

TL;DR

  • 这是什么: tests 视角审查 PR iter104 cluster-1 #1063: 删 StreamingProxyChatLifecycleFacade,fold 进 StreamingProxyGAgent #1069 删除 StreamingProxyChatLifecycleFacade 并 fold 到 StreamingProxyGAgent 后的测试覆盖。
  • 结论: reject;endpoint 侧删 facade 的覆盖已重构,但新增 typed continuation 自事件和 actor handler 没有行为测试。
  • EN: Reject because the new actor-owned StreamingProxyChatLifecycleContinuationRequested publish/consume lifecycle is not covered.
  • 下一步: 请补 GAgent 级测试,钉住 self continuation 发布、无 token 不发布、以及 continuation handler 推进 participant reply 后提交 terminal state。

详细说明

这轮改动的核心行为已经从 Application facade 转移到 actor 内部:StreamingProxyGAgent 在 chat request 带 access token 时发布 StreamingProxyChatLifecycleContinuationRequested self event,然后 HandleChatLifecycleContinuationRequested 负责 participant join、reply generation、terminal state 推进。当前测试只验证了 endpoint 不再发布 endpoint-owned terminal fallback,也把 DetermineParticipantTerminalState 这个 helper 单独测了;但没有测试断言 typed self event 被发布,也没有测试真正调用 continuation handler 并观察终态提交。

这会留下一个真实覆盖洞:如果以后 HandleChatRequest 不再 publish StreamingProxyChatLifecycleContinuationRequested,或者 HandleChatLifecycleContinuationRequested 没有调用 participant coordinator / 没有推进 terminal state,现有测试仍可能通过。建议补两个 GAgent 行为测试:一个覆盖有 token 发布 self continuation、无 token 不发布;另一个覆盖 continuation handler 在 successful replies 和 zero replies 下分别提交 completed / failed terminal state。


📎 完整 codex 原始输出(存档备查)
---
pr: 1069
role: tests
verdict: reject
---

## Verdict
Reject: the test changes reframe endpoint-side facade deletion, but they do not cover the new actor-owned typed continuation lifecycle added by the PR.

## Evidence
- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:70` publishes the new `StreamingProxyChatLifecycleContinuationRequested` self event when a chat request carries an access token, but `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:1567` only asserts persisted lifecycle fields and topic/message facts. A full test search finds no test assertion for `StreamingProxyChatLifecycleContinuationRequested`, so a regression that stops publishing the typed self continuation would pass.
- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:87` adds `HandleChatLifecycleContinuationRequested`, which is the new owner of participant join, reply generation, and terminal-state publication after deleting `StreamingProxyChatLifecycleFacade`. The updated tests only invoke `DetermineParticipantTerminalState` via reflection at `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:995` and endpoint failure/cancel paths at `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:1012`; they never exercise the handler through a real service provider/coordinator path.
- The Application-side coverage is partially reframed: endpoint tests now assert `HandleChatAsync` delegates to `ICommandInteractionService<StreamingProxyRoomChatCommand...>` and no longer emits endpoint-owned terminal commands. That is useful, but it leaves the core behavior moved from the facade into the GAgent untested.

## What would change your verdict
- Add a GAgent behavior test that calls `HandleChatRequest` with `NyxIdAccessToken` and asserts the published self event is `StreamingProxyChatLifecycleContinuationRequested` with trimmed `scope_id/session_id/prompt/access_token/preferred_route/default_model`, and a no-token case that asserts no continuation self event is published.
- Add a GAgent behavior test for `HandleChatLifecycleContinuationRequested` using a real or test-registered `StreamingProxyNyxParticipantCoordinator` dependency, asserting participant join/reply calls lead to `StreamingProxySessionTerminalStateRequested` / committed terminal state for both successful replies and zero replies.

REVIEW_DONE:1069:tests-r1:reject
⟦AI:AUTO-LOOP

REVIEW_DONE:1069:tests-r1:reject
⟦AI:AUTO-LOOP⟧

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

📊 r1 verdicts(architect+tests reject)→ fix r2

reviewer r1
architect ❌ reject(actor handler 内做 Nyx/LLM streaming I/O,违反 'External streaming I/O stays outside actor turns')
tests ❌ reject(typed continuation lifecycle 无直接 GAgent 测试)
quality comment
维度
阶段 🔧 fixing(r2)
修法 1) actor 只 publish typed continuation request;外部 adapter 消费做 Nyx I/O 2) adapter 完成后回 typed event 给 actor commit state 3) 加 GAgent 行为测试(publish + handler)
是否需要人介入 ❌ 否

⟦AI:AUTO-LOOP⟧

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

📊 codex 进展 review-pr1069-architect-r1 (⏳ 进行中; 已跑 11 min)

Verdict: `reject`

主要原因:`StreamingProxyChatLifecycleFacade` 虽然已删除,但新 `StreamingProxyGAgent.HandleChatLifecycleContinuationRequested` 在 actor handler 内 await Nyx/LLM streaming I/O 与 room command orchestration,违反 actor turn 边界和现有注释 `External Nyx streaming I/O stays outside actor turns.`

已发布评论:
https://github.com/aevatarAI/aevatar/pull/1069#issuecomment-4540295536

内部 artifact:
`.refactor-loop/runs/reviewer-architect-pr1069-r1.md`

REVIEW_DONE:1069:architect-r1:reject
⟦AI:AUTO-LOOP⟧
tokens used
82,852
已完成 Phase 8 architect r1 评审并发 PR 评论。

Verdict: `reject`

主要原因:`StreamingProxyChatLifecycleFacade` 虽然已删除,但新 `StreamingProxyGAgent.HandleChatLifecycleContinuationRequested` 在 actor handler 内 await Nyx/LLM streaming I/O 与 room command orchestration,违反 actor turn 边界和现有注释 `External Nyx streaming I/O stays outside actor turns.`

已发布评论:
https://github.com/aevatarAI/aevatar/pull/1069#issuecomment-4540295536

内部 artifact:
`.refactor-loop/runs/reviewer-architect-pr1069-r1.md`

自动更新每 10 分钟;edit-in-place 不堆评论;codex 完成后此 comment 自动删除(per Auric "完成后删掉就好了 否则太占空间")。
🤖 controller progress reporter

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

📊 codex 进展 review-pr1069-quality-r1 (⏳ 进行中; 已跑 11 min)

+- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyEndpoints.cs:241`, `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:42`, `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:89`: `// Refactor (iter104/cluster-1): ... Old pattern ... New principle ...` 内容能说明旧 facade owned continuation、新 actor typed continuation owned lifecycle,但它被压成单行并重复三次;和同文件既有 `Refactor` 注释的多行 `Old pattern` / `New principle` block 风格不一致,非审计读者扫读时可读性较差。
+- Dead code check: `StreamingProxyChatLifecycleFacade`、`StreamingProxyChatLifecycleRequest`、`StreamingProxyJoinLifecycle*`、`StreamingProxyRoomDeleteLifecycle*`、`StreamingProxySubscriptionLifecycle*` 仅剩自文档文本和测试中的 negative assertion,没有生产调用残留。
+- Naming check: `StreamingProxyChatLifecycleContinuationRequested` 清楚表达了 self continuation 的业务意图;handler `HandleChatLifecycleContinuationRequested` 与 proto message、endpoint name `continueChatLifecycle` 一致。
+- Complexity/readability check: 新增 actor handler约 35 行,流程为 token guard -> ensure participants -> generate replies -> terminal state,分支少且局部变量命名直接;endpoint 删除 facade 后未引入新抽象。
+
+## What would change your verdict
+把三处 iter104 自文档改成现有仓库风格的多行 block,例如:
+
+```csharp
+// Refactor (iter104/cluster-1):
+//   Old pattern: StreamingProxyChatLifecycleFacade owned chat continuation orchestration in Application layer.
+//   New principle: StreamingProxyGAgent typed continuation owns lifecycle; deprecated compat endpoints only normalize and dispatch typed commands.
+```
+
+REVIEW_DONE:1069:quality-r1:comment
+⟦AI:AUTO-LOOP⟧

tokens used
62,453
已完成 PR #1069 quality round 1 审查并发评审评论。

- Verdict: `comment`
- 主要意见:三处 iter104 `Refactor` 自文档建议改成多行 Old/New block;其余命名、dead code 清理、复杂度未发现阻断问题。
- Artifact: [.refactor-loop/runs/review-pr1069-quality-r1.md](/Users/auric/aevatar-wt-iter104-cluster-1-streamingproxy-delete-facade/.refactor-loop/runs/review-pr1069-quality-r1.md)
- PR comment: https://github.com/aevatarAI/aevatar/pull/1069#issuecomment-4540288884

自动更新每 10 分钟;edit-in-place 不堆评论;codex 完成后此 comment 自动删除(per Auric "完成后删掉就好了 否则太占空间")。
🤖 controller progress reporter

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

📊 codex 进展 review-pr1069-tests-r1 (⏳ 进行中; 已跑 11 min)

+## Verdict
+Reject: the test changes reframe endpoint-side facade deletion, but they do not cover the new actor-owned typed continuation lifecycle added by the PR.
+
+## Evidence
+- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:70` publishes the new `StreamingProxyChatLifecycleContinuationRequested` self event when a chat request carries an access token, but `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:1567` only asserts persisted lifecycle fields and topic/message facts. A full test search finds no test assertion for `StreamingProxyChatLifecycleContinuationRequested`, so a regression that stops publishing the typed self continuation would pass.
+- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:87` adds `HandleChatLifecycleContinuationRequested`, which is the new owner of participant join, reply generation, and terminal-state publication after deleting `StreamingProxyChatLifecycleFacade`. The updated tests only invoke `DetermineParticipantTerminalState` via reflection at `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:995` and endpoint failure/cancel paths at `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:1012`; they never exercise the handler through a real service provider/coordinator path.
+- The Application-side coverage is partially reframed: endpoint tests now assert `HandleChatAsync` delegates to `ICommandInteractionService<StreamingProxyRoomChatCommand...>` and no longer emits endpoint-owned terminal commands. That is useful, but it leaves the core behavior moved from the facade into the GAgent untested.
+
+## What would change your verdict
+- Add a GAgent behavior test that calls `HandleChatRequest` with `NyxIdAccessToken` and asserts the published self event is `StreamingProxyChatLifecycleContinuationRequested` with trimmed `scope_id/session_id/prompt/access_token/preferred_route/default_model`, and a no-token case that asserts no continuation self event is published.
+- Add a GAgent behavior test for `HandleChatLifecycleContinuationRequested` using a real or test-registered `StreamingProxyNyxParticipantCoordinator` dependency, asserting participant join/reply calls lead to `StreamingProxySessionTerminalStateRequested` / committed terminal state for both successful replies and zero replies.
+
+REVIEW_DONE:1069:tests-r1:reject
+⟦AI:AUTO-LOOP⟧

tokens used
58,115
已完成 Phase 8 tests reviewer round 1。

结论:`reject`。主要缺口是新增的 typed `StreamingProxyChatLifecycleContinuationRequested` self continuation 发布/消费链路没有被行为测试覆盖。

已发布 PR 评论:
https://github.com/aevatarAI/aevatar/pull/1069#issuecomment-4540305609

Marker:

自动更新每 10 分钟;edit-in-place 不堆评论;codex 完成后此 comment 自动删除(per Auric "完成后删掉就好了 否则太占空间")。
🤖 controller progress reporter

@codecov
Copy link
Copy Markdown

codecov Bot commented May 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.16%. Comparing base (7310add) to head (4122d97).
⚠️ Report is 27 commits behind head on auto-refact-dev.

@@                 Coverage Diff                 @@
##           auto-refact-dev    #1069      +/-   ##
===================================================
+ Coverage            83.05%   83.16%   +0.11%     
===================================================
  Files                  999     1001       +2     
  Lines                65296    65694     +398     
  Branches              8442     8489      +47     
===================================================
+ Hits                 54231    54635     +404     
+ Misses                7408     7360      -48     
- Partials              3657     3699      +42     
Flag Coverage Δ
ci 83.16% <ø> (+0.11%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.
see 17 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

loning added 2 commits May 26, 2026 13:37
…treamingProxyGAgent

Phase 9 r2 consensus(reflector r1 retry-fix narrowing → 3/3 unanimous):
- 不新 actor type;reuse existing StreamingProxyGAgent
- delete/fold StreamingProxyChatLifecycleFacade orchestration into actor handlers
- 保留 deprecated compat endpoints
- 禁 reuse LlmSessionGAgent(与 #1058 耦合)

违反 CLAUDE.md「Actor 设计」「业务推进内聚」「命令骨架内聚」:
Application 层 facade 持 chat continuation lifecycle orchestration。

修法:
- 删 StreamingProxyChatLifecycleFacade + DI 注册
- StreamingProxyEndpoints 保留 deprecated compat surface,只做 HTTP
  normalize + admission + dispatch/attach
- StreamingProxyGAgent 加 typed self continuation
  StreamingProxyChatLifecycleContinuationRequested,actor handler 内推进
  Nyx participant join/reply/terminal effects
- 不新 actor;不动 LlmSessionGAgent;不动 NyxID/chrono-* 外部

verified:
- build + AI tests (StreamingProxy filter) + Workflow Host API tests +
  Workflow SDK tests 全过
- architecture/test_stability guards 全过

closes #1063

⟦AI:AUTO-LOOP⟧
…er 做 Nyx I/O

r1 architect reject:HandleChatLifecycleContinuationRequested 在 actor handler
内调 StreamingProxyNyxParticipantCoordinator 做 Nyx/LLM streaming I/O,违反
"External Nyx streaming I/O stays outside actor turns"。

r1 tests reject:typed continuation lifecycle 无直接 GAgent 行为测试。

修法:
- 删 actor handler 内 await StreamingProxyNyxParticipantCoordinator
- actor 只 publish typed continuation request + persist actor-owned facts
- 引入 outside-actor adapter/runner(host-side consumer)消费 typed continuation
- adapter 完成 participant join/reply/terminal → typed event 回 actor commit state
- 加 GAgent 行为测试:publish continuation(有/无 token)+ continuation handler

verified:
- build + AI tests + Workflow Host + Workflow SDK tests 全过
- architecture/test_stability guards 全过

⟦AI:AUTO-LOOP⟧
@loning loning force-pushed the iter104/cluster-1-streamingproxy-delete-facade branch from da598d5 to 4122d97 Compare May 26, 2026 05:37
@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

📊 fix r2 已提交 → 派 r2 reviewers × 3

维度
阶段 👀 phase:reviewing(r2)
commit actor 只 publish continuation + 引入 outside-actor adapter/runner
验证 build + AI/Workflow/Voice tests 全过 + guards 过
是否需要人介入 ❌ 否

⟦AI:AUTO-LOOP⟧

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

🤖 Phase 8 Reviewer — tests (round 2)

TL;DR

  • 中文: tests 视角本轮可以 approve;r1 缺口已经用直接 GAgent 行为测试补上。
  • English: approve from the tests angle; the round-1 gap is covered by direct GAgent behavior tests.
  • 中文: 覆盖了有 token 发布 self continuation、无 token 不发布、compat handler 转发、runner 直接推进,以及 runner 从 stream 消费。
  • English: coverage includes token/no-token publish behavior, compat handler forwarding, direct runner execution, and stream consumption.
  • 中文: 下一步 controller 可以继续收敛其他 reviewer 结论;我这里没有 tests blocker。
  • English: controller can proceed with the other reviewer signals; no tests blocker from this role.

详细说明

这轮新增的 GAgent_HandleChatRequest_WithNyxToken_ShouldPublishTypedContinuationToRunner 断言了 ChatRequestEvent 中的 token、route、model、scope、session、prompt 被修剪后通过 SendToAsync 发到 StreamingProxyGAgent.ChatLifecycleContinuationRunnerStreamIdGAgent_HandleChatRequest_WithoutNyxToken_ShouldNotPublishContinuation 断言无 token 时既不 SendToAsync,也不误走普通 publish。

GAgent_HandleChatLifecycleContinuationRequested_ShouldForwardCompatRequestToRunner 覆盖了旧 handler 入口继续转发到 runner stream。runner 侧的 ChatLifecycleContinuationRunner_ShouldCommitCompletedTerminalState_WhenRepliesSucceedChatLifecycleContinuationRunner_ShouldCommitFailedTerminalState_WhenNoReplies 分别验证成功终态和零 reply 失败终态;ChatLifecycleContinuationRunner_ShouldConsumeTypedContinuationFromRunnerStream 验证 hosted runner 订阅 stream 后能消费 typed continuation 并推进到 terminal command。

避坏模式检查没有发现新增 Task.DelayThread.Sleep[Skip]、manual trait、空断言或明显弱化断言。handler 转发测试只断言类型和目标 stream,payload 保真主要由 token 发布测试覆盖;作为 tests reviewer 我不把它升级为 blocker。

📎 完整 codex 原始输出(存档备查)
---
pr: 1069
role: tests
verdict: approve
---

## Verdict
Approve: round 2 adds the missing direct GAgent behavior coverage for typed continuation publish/consume, and terminal success/zero-reply outcomes are asserted.

## Evidence
- `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:1628` covers `ChatRequestEvent` with Nyx token publishing a `StreamingProxyChatLifecycleContinuationRequested` via `SendToAsync` to `StreamingProxyGAgent.ChatLifecycleContinuationRunnerStreamId`, with trimmed session/scope/prompt/token/route/model asserted.
- `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:1667` covers the no-token branch and asserts no continuation is sent or published.
- `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:1687` covers the compat continuation handler forwarding the typed request to the runner stream.
- `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:1709` and `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:1741` cover runner advancement to terminal `Completed` and zero-reply `Failed` states.
- `test/Aevatar.AI.Tests/StreamingProxyCoverageTests.cs:1774` covers the hosted runner consuming a typed continuation from the runner stream and producing the terminal command.
- No added `Task.Delay`, `Thread.Sleep`, `[Skip]`, manual trait, empty assertion, or assertion-loosening pattern was found in the touched test files.

## What would change your verdict
N/A.

REVIEW_DONE:1069:tests-r2:approve

REVIEW_DONE:1069:tests-r2:approve
⟦AI:AUTO-LOOP⟧

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

📊 codex 进展 review-pr1069-tests-r2 (⏳ 进行中; 已跑 4 min)

## What would change your verdict
N/A.

REVIEW_DONE:1069:tests-r2:approve

REVIEW_DONE:1069:tests-r2:approve
⟦AI:AUTO-LOOP⟧
POST_EOF
gh pr comment 1069 --body-file "$BODY"' in /Users/auric/aevatar-wt-iter104-cluster-1-streamingproxy-delete-facade
succeeded in 1164ms:
#1069 (comment)

codex
已完成 Phase 8 tests round 2 review,并已发 PR 评论:

#1069 (comment)

结论:approve。本轮补上了 GAgent 有 token / 无 token continuation 行为、compat handler 转发、runner 直接推进、runner 从 stream 消费,以及成功 / 零 reply terminal state 验证;未发现 tests 侧 blocker。
tokens used
70,578
已完成 Phase 8 tests round 2 review,并已发 PR 评论:


> 自动更新每 10 分钟;edit-in-place 不堆评论;**codex 完成后此 comment 自动删除**(per Auric "完成后删掉就好了 否则太占空间")。
🤖 controller progress reporter

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

🤖 Phase 8 Reviewer — quality (round 2)

TL;DR


详细说明

我按三点 diff 看了 touched files。StreamingProxyChatLifecycleContinuationRunner 这个名字虽然长,但准确表达了“host-side runner 消费 chat lifecycle continuation request”的职责;HandleChatLifecycleContinuationRequested 里的 r2 注释也清楚说明了旧模式是 actor turn 内等待 Nyx/LLM streaming I/O,新模式是 actor 只把 typed request 转给 host runner。删除 StreamingProxyChatLifecycleFacade 后没有看到明显残留的 public receipt/request 类型或调用点。

唯一质量 comment:agents/Aevatar.GAgents.StreamingProxy/StreamingProxyChatLifecycleContinuationRunner.cs:14 注入并保存了 _streamProvider,但生产代码里只在 StartAsync:37 调用 _streamProvider.GetStream(...) 后丢弃返回值。紧接着 IActorEventSubscriptionProvider(host-side typed subscription abstraction)已经用相同 stream id 订阅;现有实现本身会通过 stream provider resolve stream。这个额外字段/构造参数看起来像 no-op 预热,增加了 runner 的依赖面和测试装配噪音。建议删除 _streamProvider 依赖和 GetStream 调用;如果底层确实需要显式预热,请在该行旁边用一句 why-comment 说明具体 runtime 约束。


📎 完整 codex 原始输出(存档备查)

pr: 1069
role: quality
verdict: comment

Verdict

整体质量可接受,但 StreamingProxyChatLifecycleContinuationRunner 里有一个看起来多余的 stream 预取依赖,建议清理或解释。

Evidence

  • agents/Aevatar.GAgents.StreamingProxy/StreamingProxyChatLifecycleContinuationRunner.cs:14 / :37: _streamProvider 只用于 StartAsync 中调用 GetStream(...),返回值被丢弃;随后 _subscriptionProvider.SubscribeAsync(...) 已经负责按同一 stream id 订阅。这个依赖目前像 dead/no-op setup,扩大构造参数和测试装配面。
  • agents/Aevatar.GAgents.StreamingProxy/StreamingProxyChatLifecycleContinuationRunner.cs:9: r2 self-doc 清楚解释了 actor turn 内等待 Nyx streaming I/O 的旧模式,以及 host-side runner 的新边界。
  • agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:98: actor handler scope 的 self-doc 清楚,且 handler 只做 access token guard、room id fallback、typed request forward,复杂度低。
  • agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:24: ChatLifecycleContinuationRunnerStreamId 命名表达用途,没有看到字符串路由在多处散落。
  • agents/Aevatar.GAgents.StreamingProxy/StreamingProxyChatLifecycleFacade.cs: 删除后未看到生产调用残留;测试里保留的 NotContain 断言是覆盖删除意图,不是 dead code。

What would change your verdict

删除 StreamingProxyChatLifecycleContinuationRunner_streamProvider 字段、构造参数和 StartAsync 中的丢弃式 GetStream(...) 调用;如果它是特定 runtime 所需的显式初始化,则保留但补一行说明为什么 SubscribeAsync 不能承担这一步。

REVIEW_DONE:1069:quality-r2:comment
⟦AI:AUTO-LOOP⟧

REVIEW_DONE:1069:quality-r2:comment
⟦AI:AUTO-LOOP⟧

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

🤖 Phase 8 Reviewer — architect (round 2)

TL;DR

  • 结论 / Verdict: approve,r2 已把 Nyx/LLM streaming I/O 从 actor turn 移到 host-side runner。
  • 架构检查 / Architecture check: actor 现在只提交自身事实并发送 typed continuation;runner 通过 typed room commands 回写结果。
  • 需要 maintainer 做什么 / Maintainer action: architect 角度无需继续修改,可交给其他 reviewer/CI 结论。

详细说明

本轮重点复核 r1 blocker:HandleChatLifecycleContinuationRequested 不应在 actor handler 内 await StreamingProxyNyxParticipantCoordinator 做 Nyx/LLM streaming I/O。r2 后,StreamingProxyGAgentHandleChatRequest 中先 PersistDomainEventAsync 持久化 actor-owned lifecycle/topic facts,然后通过 SendToAsync 发送 StreamingProxyChatLifecycleContinuationRequested;compat handler 也只补齐 RoomId 并转发同一个 typed request,没有直接调用 coordinator。

StreamingProxyChatLifecycleContinuationRunner 是通过 IHostedService 注册的 host-side runner,不在 actor turn 内运行。它在 StartAsync 里订阅 runner stream,RunAsync 里执行 EnsureParticipantsJoinedAsync / GenerateRepliesAsync,最后经 IStreamingProxyRoomCommandService.PublishTerminalStateAsync 发 typed room command 回 room actor。这里的 IStreamingProxyRoomCommandService 是 room actor 的命令入口,不是直接 state mutation;真正 committed terminal fact 仍由 StreamingProxyGAgent.HandleSessionTerminalStateRequested 处理并持久化。

核对点:

检查项 结论
actor handler 是否只 publish + persist 通过。StreamingProxyGAgent.cs:68-87 persist facts + SendToAsyncStreamingProxyGAgent.cs:95-109 compat handler 只转发 typed continuation。
continuation runner 是否在 actor turn 外 通过。ServiceCollectionExtensions.cs:33-36 注册为 IHostedServiceStreamingProxyChatLifecycleContinuationRunner.cs:35-41 在 host start 时订阅。
runner 到 actor reply 是否 typed event/command 通过。StreamingProxyChatLifecycleContinuationRunner.cs:82-107 外部 I/O 后调用 typed room command;StreamingProxyGAgent.cs:218-231 由 actor commit terminal state。
self-doc 是否清晰 通过。新 runner 和 compat handler 都有 r2 old/new pattern comment,说明 actor-turn I/O 被移出。
📎 完整 codex 原始输出(存档备查)
---
pr: 1069
role: architect
verdict: approve
---

## Verdict
approve — r2 removes the r1 actor-turn Nyx/LLM streaming I/O violation by moving continuation execution to a hosted runner and returning outcomes through typed room commands.

## Evidence
- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:68` persists `StreamingProxyChatLifecycleAcceptedEvent`, and `:69` persists `GroupChatTopicEvent`; the actor owns lifecycle/topic facts before any continuation is requested.
- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:76` sends `StreamingProxyChatLifecycleContinuationRequested` to `ChatLifecycleContinuationRunnerStreamId`; no `StreamingProxyNyxParticipantCoordinator` call remains in `HandleChatRequest`.
- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:95`-`:109` keeps the compat `continueChatLifecycle` handler as a typed republish only. This satisfies the CLAUDE/AGENTS rule: "External Nyx streaming I/O stays outside actor turns" and "跨 actor 等待必须 continuation 化".
- `agents/Aevatar.GAgents.StreamingProxy/ServiceCollectionExtensions.cs:35` registers `StreamingProxyChatLifecycleContinuationRunner` as `IHostedService`, and `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyChatLifecycleContinuationRunner.cs:35`-`:41` subscribes from host startup. This confirms the continuation consumer is outside the actor event handler.
- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyChatLifecycleContinuationRunner.cs:82`-`:98` is the only reviewed path that awaits Nyx participant join/reply streaming, and it lives in the hosted runner rather than the actor.
- `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyChatLifecycleContinuationRunner.cs:100`-`:107` returns terminal outcome through `IStreamingProxyRoomCommandService.PublishTerminalStateAsync`; `agents/Aevatar.GAgents.StreamingProxy/StreamingProxyGAgent.cs:218`-`:231` commits the typed terminal request inside the actor, so there is no direct actor state mutation from the runner.
- `agents/Aevatar.GAgents.StreamingProxy/streaming_proxy_messages.proto:74`-`:82` adds a new request message with new field numbers only; no field renumber/reuse observed.
- Anti-pattern grep on the PR diff did not find new `actor.HandleEventAsync(`, `Task.Delay(` in production, `GetAwaiter().GetResult()`, `TypeUrl.Contains(...)`, `[Skip]`, raw `HttpClient` constructor in production, or host/application `SubscribeAsync<EventEnvelope>` use.

## What would change your verdict
N/A from architect angle.

REVIEW_DONE:1069:architect-r2:approve

REVIEW_DONE:1069:architect-r2:approve
⟦AI:AUTO-LOOP⟧

@loning
Copy link
Copy Markdown
Contributor Author

loning commented May 26, 2026

✅ Phase 8 共识达成(r2 round)— 2 approve + 1 comment

reviewer r1 r2
architect ❌ reject ✅ approve
tests ❌ reject ✅ approve
quality comment comment(non-blocking)

结果:CI 验证通过后 auto-merge。

维度
阶段 ⚙️ phase:ci-running
是否需要人介入 ❌ 否,CI 绿后 controller auto-merge

⟦AI:AUTO-LOOP⟧

@loning loning merged commit 3b4a255 into auto-refact-dev May 26, 2026
12 checks passed
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.

1 participant