Skip to content

Add Team-scoped EventEnvelope topology read model and API #737

Closed

Description

@potter-sun
id: cluster-triage-team-event-envelope-topology
severity: medium
requires_design: true

来源

本 issue 由 maintainer 手动开,triage codex 深度调研补 evidence。

核心问题

Team 维度当前只有 StudioTeamCurrentStateDocument summary readmodel 和 members 查询;EventEnvelope 的 topology route primitive 与 workflow run graph 已存在,但没有 Team-scoped EventEnvelope topology readmodel/API。若前端把 /api/actors/{actorId}/graph-enriched 或多个 run graph 临时拼成 Team 事件拓扑,会把单次 run artifact、runtime topology 和 Team 长期协作事实混在一起,违反查询走 readmodel、projection 消费 committed/durable facts、版本诚实暴露的架构边界。

Evidence

1. Team current-state readmodel 不承载 event topology

src/Aevatar.Studio.Projection/ReadModels/studio_projection_readmodels.proto:188

// ─── StudioTeam Current State ReadModel ───
//
// Materialized from StudioTeamGAgent committed events (ADR-0017). Document id
// is the actor id (<c>studio-team:{scopeId}:{teamId}</c>). scope_id is the
// prefix used by query ports to list teams per scope. The full member roster
// is intentionally NOT mirrored — listing members goes through the member
// read model filtered by team_id (see ADR-0017 Non-Goals).

message StudioTeamCurrentStateDocument {
  string id = 1;
  string actor_id = 2;
  int64 state_version = 3;
  string last_event_id = 4;
  google.protobuf.Timestamp updated_at = 5;
  ...
  int32 member_count = 30;
  optional string entry_member_id = 40;
}

违反点: Team readmodel 只有 summary/current-state 字段;Team event topology 的 node/edge/status/count/lastSeen/version 还没有 typed contract,不能让 UI 在 query-time 从多个来源拼装业务事实。

2. Team projector 只物化 StudioTeamState summary

src/Aevatar.Studio.Projection/Projectors/StudioTeamCurrentStateProjector.cs:45

if (!CommittedStateEventEnvelope.TryUnpackState<StudioTeamState>(
        envelope,
        out _,
        out var stateEvent,
        out var state) ||
    stateEvent?.EventData == null ||
    state == null)
{
    return;
}

var document = new StudioTeamCurrentStateDocument
{
    Id = context.RootActorId,
    ActorId = context.RootActorId,
    StateVersion = stateEvent.Version,
    LastEventId = stateEvent.EventId ?? string.Empty,
    UpdatedAt = Timestamp.FromDateTimeOffset(updatedAt),
    TeamId = state.TeamId,
    ScopeId = state.ScopeId,
    DisplayName = state.DisplayName,
    Description = state.Description,
    LifecycleStage = TeamLifecycleStageMapper.ToWireName(state.LifecycleStage),
    CreatedAt = state.CreatedAtUtc,
    MemberCount = state.MemberIds.Count,
};

违反点: 现有 materializer 是 Team actor current-state summary;把跨 actor EventEnvelope flow 长期塞到该 projector 里会把 projection 变成第二套业务计算框架。Team topology 需要独立 readmodel/materializer,或先由明确 aggregate actor 拥有跨 actor topology 事实。

3. Team HTTP route group 没有 topology 查询入口

src/Aevatar.Studio.Hosting/Endpoints/StudioTeamEndpoints.cs:35

app.MapPost("/api/scopes/{scopeId}/teams", HandleCreateAsync)
    .WithTags("StudioTeams");
app.MapGet("/api/scopes/{scopeId}/teams", HandleListAsync)
    .WithTags("StudioTeams");
app.MapGet("/api/scopes/{scopeId}/teams/{teamId}", HandleGetAsync)
    .WithTags("StudioTeams");
app.MapPatch("/api/scopes/{scopeId}/teams/{teamId}", HandlePatchAsync)
    .WithTags("StudioTeams");
...
app.MapGet(
        "/api/scopes/{scopeId}/teams/{teamId}/members",
        HandleListMembersAsync)
    .WithTags("StudioTeams");

违反点: Team route 已有 create/list/get/patch/archive/entry-member/members,但没有 /event-topology。新增 endpoint 必须继续通过 Application service/query port 组合,Host 不能直接读 projection store、actor runtime 或 event store。

4. 现有 graph-enriched 是 actor-rooted graph,不是 Team topology

apps/aevatar-console-web/src/shared/api/runtimeActorsApi.ts:56

getActorGraphEnriched(
  actorId: string,
  options?: ActorGraphOptions
): Promise<WorkflowActorGraphEnrichedSnapshot> {
  return requestJson(
    withQuery(`/api/actors/${encodeURIComponent(actorId)}/graph-enriched`, {
      depth: options?.depth,
      take: options?.take,
      direction: options?.direction,
      edgeTypes: normalizeEdgeTypes(options?.edgeTypes),
    }),
    decodeWorkflowActorGraphEnrichedResponse
  );
},

违反点: 该 API 以 actorId 为 root,适合 actor/run drilldown;Team Detail 事件拓扑 需要以 scopeId + teamId 为资源身份,不能把 run graph 命名成 Team topology。

5. workflow topology 已有反 side-read 先例

src/workflow/Aevatar.Workflow.Projection/Projectors/WorkflowExecutionArtifactMaterializationSupport.cs:136

// Refactor (iter33/cluster-035-workflow-report-runtime-topology-sideread):
//   Old pattern: Workflow report 用 IActorRuntime.GetAsync(...).GetChildrenIdsAsync() 读 runtime children 当 topology 事实,违反 runtime-shape-not-fact + side-read
//   New principle: 删 IWorkflowExecutionTopologyResolver + ActorRuntimeWorkflowExecutionTopologyResolver;topology 从 committed event projection 来(WorkflowRoleActorLinkedEvent + SubWorkflowBindingUpsertedEvent 已 materialize);enum 值 RuntimeSnapshot 改 CommittedProjection;无 proto 改
TopologySource = WorkflowExecutionTopologySource.CommittedProjection,

违反点: 仓库已经删除过 runtime topology side-read。Team topology 设计必须从 committed/durable projection feed 物化,不能回退到 runtime children、event store replay 或 query-time graph stitching。

6. EventEnvelope topology publication 是 route primitive,不是业务 readmodel

src/Aevatar.Foundation.Abstractions/agent_messages.proto:44

message EventEnvelope {
  string                      id          = 1;
  google.protobuf.Timestamp   timestamp   = 2;
  google.protobuf.Any         payload     = 3;
  EnvelopeRoute               route       = 4;
  EnvelopePropagation         propagation = 5;
  EnvelopeRuntime             runtime     = 6;
}

message PublicationRoute {
  oneof audience {
    TopologyPublication topology = 1;
    ObserverPublication observer = 2;
  }
}

违反点: TopologyPublication 只能表达 route audience;Team topology 的 node kind、edge kind、event type、status、count、lastSeen、watermark 都是稳定查询语义,必须 typed proto/readmodel 建模,不能直接暴露 runtime route shape 或塞进 generic bag。

违反条款

CLAUDE.md:

  • 统一投影链路:CQRS 与 AGUI 走同一套 Projection Pipeline,统一入口、一对多分发,禁止双轨实现。
  • 读写分离:Command -> Event,Query -> ReadModel;异步完成通过事件通知,不在会话内拼装流程。
  • 核心语义强类型:影响业务语义、控制流、稳定读取且仓库内可控的数据,必须建模为 proto field / typed option / typed sub-message,禁止塞入通用 bag。
  • 查询始终走 readmodel:对外查询只读 readmodel;不暴露 actor 内部状态、state mirror payload 或 event replay 为查询主路径。
  • 禁止 query-time replay/priming:QueryPort/QueryService/ApplicationService不得在请求路径读IEventStore、重放 events、临时重建 state mirror,或在 query 方法内同步补投影/补跑 ES/materialization;刷新须通过正式 projection 会话、后台 materializer 或写侧预挂接 projection 完成。
  • ``EventEnvelope是唯一投影传输壳:业务消息与投影消息都用EventEnvelope`;区别由强类型 payload 表达,禁止引入第二层包络。`
  • readmodel 按需创建:只有存在稳定消费场景(明确消费方、查询入口、返回 DTO)时才新增 readmodel。
  • 聚合必须 actor 化:跨 actor 聚合/汇总/关联若有稳定业务语义,建模为 aggregate actor;禁止长期放在 query-time 拼装层。
  • projection 只消费 committed 事实:基于 committed domain event 或其同源 durable feed 构建;禁止订阅入站 command、self continuation 或 actor 运行时偶然结构。
  • 版本对齐权威源:readmodel 版本必须来自权威 actor 的 committed version 或等价水位;禁止本地 projection counter 或 StateVersion++ 冒充权威版本。
  • 新增状态/事件/持久化载荷:先定义 .proto 并生成类型,再接入实现;禁止先写临时结构后补 Protobuf。

AGENTS.md:

  • 新增 readmodel 必须先声明消费场景:同一 actor 可以对应多个 readmodel,但每个 readmodel 都必须有明确的消费方、查询入口、返回 DTO 或 UI/搜索/图查询场景;没有稳定消费场景的 readmodel 不得新增。
  • projection 负责读模型物化:Projection Pipeline 的默认职责是消费 actor 发布的 EventEnvelope,再基于其中的 state_event + state_root 物化到多个 document/index/search/graph store;禁止把 projection 作为第二套通用业务计算框架。
  • 不得 query-time priming:查询前若需要先“确保投影存在/刷新 read model”,该动作必须在显式 activation、lease、binder 或后台物化流程中完成;禁止在 query 方法内同步补投影。
  • 所有序列化与反序列化操作统一使用 Protobuf,尤其是 State、领域事件、命令、回调载荷、快照、缓存载荷、跨 Actor/跨节点内部传输对象。

docs/canon:

  • CQRS 与 AGUI/SSE/WS 共用统一 Projection 输入链路。
  • 同一 EventEnvelope 分发到多个 projector 时采用“一对多全分支尝试”语义:单个 projector 失败不阻断其他 projector 执行,最终以聚合异常统一回传。
  • 禁止 query-time replay / bootstrap:query 方法不得读取 IEventStore、重放事件、临时重建 actor state 或补跑 projection 后再返回。
  • Bootstrap 输入只来自 committed domain event、committed state publication 或同源 durable feed;不得订阅 command、self continuation 或 actor 内部 state mirror 临时结构推测完成态。
  • 同一条 EventEnvelope 并行进入多个 projector:ReadModel 分支(查询用)和 AGUI 分支(实时输出用)
  • 需要列表/统计等读模型时,扩展 reducer/projector + read-only store,通过 Query API 暴露

新原则

Team event topology 是 Team-scoped 查询语义,不是 actor runtime graph 的别名。它必须以 scopeId + teamId 为资源身份,从 committed/durable observation feed 物化 typed nodes/edges,并暴露权威水位或刷新时间。

若 topology 跨多个 actor 聚合后具有稳定业务语义,先明确唯一权威 owner:要么由 Team/aggregate actor 提交 topology facts,再投影;要么把 materializer 限定为 committed facts 的幂等覆盖副本,禁止 query-time 推导新事实。

Console Web 只能消费 Team topology API;/api/actors/{actorId}/graph-enriched 保留为 run/actor drilldown,不得作为 Team topology fallback。

Fix boundary

建议 scope paths:

  • src/Aevatar.Studio.Application/Studio/Contracts/TeamContracts.cs
  • src/Aevatar.Studio.Application/Studio/Abstractions/IStudioTeamService.cs
  • src/Aevatar.Studio.Application/Studio/Abstractions/IStudioTeamQueryPort.cs
  • src/Aevatar.Studio.Application/Studio/Services/StudioTeamService.cs
  • src/Aevatar.Studio.Projection/ReadModels/studio_projection_readmodels.proto
  • src/Aevatar.Studio.Projection/ReadModels/*.Partial.cs
  • src/Aevatar.Studio.Projection/Projectors/*Team*Projector*.cs
  • src/Aevatar.Studio.Projection/QueryPorts/ProjectionStudioTeamQueryPort.cs
  • src/Aevatar.Studio.Projection/DependencyInjection/ServiceCollectionExtensions.cs
  • src/Aevatar.Studio.Hosting/Endpoints/StudioTeamEndpoints.cs
  • test/Aevatar.Studio.Tests/*Team*Tests.cs
  • apps/aevatar-console-web/src/shared/api/runtimeRunsApi.ts
  • apps/aevatar-console-web/src/shared/api/runtimeDecoders.ts
  • apps/aevatar-console-web/src/shared/api/models.ts
  • apps/aevatar-console-web/src/pages/teams/detail.tsx
  • apps/aevatar-console-web/src/pages/teams/detail.test.tsx

避免 scope:

  • 不改外部 sibling repositories。
  • 不把 /api/actors/{actorId}/graph-enriched 改名成 Team topology。
  • 不在 Host/API 直接读取 IEventStore、actor runtime children、projection store implementation details。

Decision questions

  1. Team event topology 的权威 owner 是 StudioTeamGAgent 直接提交 facts,还是新增 Team topology aggregate actor 拥有跨 actor 聚合事实?
  2. Event attribution 到 Team 的最小 committed input 是哪些 typed facts:member team_id、published service binding、run registration、connector binding、LLM/tool invocation facts,还是先做 member/service edge 的最小切片?
  3. StudioTeamEventTopologyDocument 是 document readmodel 为主,还是同时写 IProjectionGraphStore 支持图查询?若两者都有,哪个是 API 返回的权威 surface?
  4. Edge 的 count/lastSeen/lastStatus/lastError 版本来自哪个 actor committed version 或 durable watermark,避免本地 projection counter?
  5. 前端空状态如何区分 Team 存在但无已物化 event、Team 不存在、projection stale 三种情况?

original_authors

为遵守共享规则,GitHub 正文不生成 @-mention。

  • studio_projection_readmodels.proto:188-219: eanzhao, Jason Wang
  • StudioTeamCurrentStateProjector.cs:45-79: eanzhao, Jason Wang
  • StudioTeamEndpoints.cs:35-62: eanzhao, Jason Wang
  • WorkflowExecutionArtifactMaterializationSupport.cs:136-139: author omitted by shared no-mention/private-name rule

📢 cc: 相关作者请 sanity-check Team topology owner 与 projection materialization 边界。

⟦AI:AUTO-LOOP⟧

Activity

  1. added
    auto-loop-triageExternal issue: triage codex evaluates
    auto-loopCreated by codex-refactor-loop skill
    phase9-auto-solveOperator opted this design issue into Phase 9 auto-solve
    refactor-design-neededCluster flagged requires_design by codex-refactor-loop auto audit
    and removed
    auto-loop-triageExternal issue: triage codex evaluates
    on May 29, 2026
  2. loning commented on May 31, 2026

    @loning
    Contributor

    🤖 Triage codex — accept: cluster-triage-team-event-envelope-topology

    TL;DR

    • 这是什么: Add Team-scoped EventEnvelope topology read model and API #737 是 Team-scoped EventEnvelope topology readmodel/API 的架构设计 issue,不是普通产品 feature。
    • 结论: accept;调研找到 6 处 evidence,核心缺口是 Team summary readmodel、workflow/run graph、EventEnvelope route primitive 三者已有,但没有 Team 维度 topology 查询事实。
    • 下一步: Phase 9 solver 需要先确定 topology 权威 owner,再按 Projection Pipeline 物化 typed nodes/edges。

    📢 cc 相关作者: 请 sanity-check Team topology owner 与 projection materialization 边界。


    详细说明

    当前 StudioTeamCurrentStateDocument 只表达 Team identity/lifecycle/member_count/entry member;/api/scopes/{scopeId}/teams/{teamId}/members 走 member readmodel filter;/api/actors/{actorId}/graph-enriched 是 actor/run drilldown,不是 Team 长期事件流转事实。workflow 侧已经有历史 refactor 删除 IActorRuntime.GetChildrenIdsAsync() topology side-read,Team topology 不能回退到 runtime graph 或 query-time stitching。

    推荐 cluster: cluster-triage-team-event-envelope-topology。建议最小方向是新增 Team-scoped typed readmodel/API,消费 committed/durable facts,返回 scopeId/teamId/stateVersion/updatedAt/nodes/edges,并明确 Team topology 的唯一权威 owner 或 aggregate owner;Host 只组合 Application query service,不直接读 actor runtime、event store 或 projection store 实现。


    📎 investigation artifact 摘要
    • token: Team, EventEnvelope, topology, readmodel, API
    • intersection:
      • Team + EventEnvelope: 无
      • Team + topology: 无
      • Team + readmodel: src/Aevatar.Studio.Projection/ReadModels/studio_projection_readmodels.proto
      • EventEnvelope + topology: src/Aevatar.Foundation.Abstractions/agent_messages.proto
      • readmodel + topology: src/workflow/Aevatar.Workflow.Projection/workflow_projection_transport.proto
    • evidence:
      • src/Aevatar.Studio.Projection/ReadModels/studio_projection_readmodels.proto:188 Team readmodel 没有 topology fields
      • src/Aevatar.Studio.Projection/Projectors/StudioTeamCurrentStateProjector.cs:45 projector 只物化 StudioTeamState summary
      • src/Aevatar.Studio.Hosting/Endpoints/StudioTeamEndpoints.cs:35 Team endpoints 没有 /event-topology
      • apps/aevatar-console-web/src/shared/api/runtimeActorsApi.ts:56 graph-enriched 是 actor-rooted
      • src/workflow/Aevatar.Workflow.Projection/Projectors/WorkflowExecutionArtifactMaterializationSupport.cs:136 workflow topology 已禁止 runtime side-read
      • src/Aevatar.Foundation.Abstractions/agent_messages.proto:44 TopologyPublication 是 route primitive,不是 business readmodel
    • key rules:
      • 查询始终走 readmodel:对外查询只读 readmodel;不暴露 actor 内部状态、state mirror payload 或 event replay 为查询主路径。
      • ``EventEnvelope是唯一投影传输壳:业务消息与投影消息都用EventEnvelope`;区别由强类型 payload 表达,禁止引入第二层包络。`
      • 新增 readmodel 必须先声明消费场景:同一 actor 可以对应多个 readmodel,但每个 readmodel 都必须有明确的消费方、查询入口、返回 DTO 或 UI/搜索/图查询场景;没有稳定消费场景的 readmodel 不得新增。

    ⟦AI:AUTO-LOOP⟧

  3. loning commented on May 31, 2026

    @loning
    Contributor

    solver: delete
    issue: 737
    cluster: phase9-issue737
    verdict: propose

    Classification

    (b) Orphan feature

    Recommended action

    建议不要新增 Team-scoped EventEnvelope topology readmodel/API,直接关闭 #737 这条新增能力诉求,并把“Team 详情页需要事件拓扑”的旧叙事从工作计划中移除。当前代码已经把 Team detail 的 topology/events/connectors 旧 tab 收敛回 overview,并且测试明确断言“事件拓扑”按钮不存在;现有 /api/actors/{actorId}/graph-enriched 仍作为 actor/run drilldown 保留。若用户需要追查某个成员或 run 的关系图,应从 Team 成员/运行入口跳到现有 Actor Topology,而不是为 Team 再造跨 actor 长期拓扑聚合。

    Concrete plan (if propose)

    • Files to delete: none; issue is an unimplemented feature request, not a live code path.
    • Caller migrations: apps/aevatar-console-web/src/shared/navigation/teamRoutes.ts already maps legacy tab=topology / tab=events to overview; keep this behavior. Team member/run drilldown should continue to use existing actor topology routes and runtimeActorsApi.getActorGraphEnriched(actorId).
    • Tests to delete: none. Existing tests are useful because they lock the deletion decision:
      • apps/aevatar-console-web/src/shared/navigation/teamRoutes.test.ts asserts legacy topology/events tabs fall back to overview.
      • apps/aevatar-console-web/src/pages/teams/detail.test.tsx asserts the “事件拓扑” button is absent.
    • LOC delta: -0 for production code; optional documentation cleanup can remove stale backlog mentions in docs/design/2026-04-08-aevatar-product-definition.md and docs/design/2026-04-20-studio-member-workbench-prd.md.
    • First slice: no-new-abstraction deletion/collapse slice: close Add Team-scoped EventEnvelope topology read model and API #737 as not needed, keep Team detail limited to summary + members, and keep legacy topology deep links falling back to overview.
    • Later design slice: none. Do not park a future Team topology readmodel; if a concrete UI/API consumer reappears, it should open a new issue with the exact consuming workflow and authoritative owner.

    Reverse-evidence (why this is safe to delete)

    • No public API breaks: git grep found no /event-topology, StudioTeamEventTopology, TeamEventTopology, or Team topology API/DTO in src, apps, or test.
    • No live frontend dependency: TeamDetailTab is only overview | members; legacy connectors/events/topology deep links already fall back to overview.
    • No user-facing Team topology control: TeamDetailPage tests assert screen.queryByRole("button", { name: "事件拓扑" }) is null.
    • Existing replacement path remains: actor/run graph drilldown still calls /api/actors/{actorId}/graph-enriched through runtimeActorsApi.getActorGraphEnriched(actorId), with callers in Actors, MissionControl, chat, and related tests.
    • No external repo dependency on the code: this plan does not require NyxID, chrono-storage, or chrono-ornn changes.
    • Tests covering the path are not load-bearing for a Team topology feature because the feature has no implementation; the current tests are load-bearing for the opposite behavior, namely that Team detail does not expose that topology tab.
    • Git history supports the deletion direction: the Team route originally allowed topology/events, then later commits changed the default to overview, removed those tab variants, and added tests that legacy deep links fall back to overview.

    Risks

    • Product may still want a first-class “Team collaboration canvas,” but current code has no active Team detail entry point and no API consumer that forces this readmodel now.
    • Documentation still contains aspirational references to full Team topology; if those docs are treated as committed product scope, closing Add Team-scoped EventEnvelope topology read model and API #737 should be paired with doc cleanup.
    • If a hidden external client already expects a not-yet-implemented /event-topology route, that expectation is not discoverable from this repository because no route, contract, or generated client exists.

    Escalation triggers (if any)

    • ESCALATE_REASON: deletion-affects-active-experiment if there is an active off-repo experiment or unreleased branch that consumes a Team-scoped topology API.
    • ESCALATE_REASON: deletion-touches-product-commitment if docs/design aspirational Team topology has been promoted to contractual roadmap outside this repository.

    Reasoning trace (internal — for meta-judge)

    • Why I claim this can be deleted: the requested capability is absent, its old Team detail entry points have already been deleted, legacy route state collapses to overview, and actor-rooted graph drilldown remains available for actual topology inspection.
    • What I checked to verify safety: gh issue view 737 --comments; .refactor-loop/runs/audit-iter-832.md; CLAUDE.md deletion-first clause; Team readmodel/projector/endpoints; frontend Team routes/detail tests; actor graph callers; git grep for event-topology and Team topology DTO/API names; git history for topology in Team routes and StudioTeamCurrentStateDocument.
    • What I cannot decide alone: whether an off-repo product commitment requires Team topology despite the current repo having no route, DTO, UI entry, or tests for it.

    SOLVER_DONE:delete:propose:close-issue737-and-keep-team-detail-summary-members-only:first-slice=close-feature-request-no-new-abstraction
    ⟦AI:AUTO-LOOP⟧

  4. loning commented on May 31, 2026

    @loning
    Contributor

    🤖 Structural solver — escalate aggregate owner, keep first slice narrow

    TL;DR

    • 这是什么: Add Team-scoped EventEnvelope topology read model and API #737 的结构解法评估,重点是 Team-scoped EventEnvelope topology 不能从 actor graph/run graph 临时拼出来。
    • 结论是什么: 建议先落一个无新抽象的 Team topology readmodel/API 空壳切片;真正跨 actor EventEnvelope flow 需要 aggregate owner 决策,所以 verdict=escalate。
    • 需要 maintainer 做什么: 决定跨 actor Team topology 的权威 owner 是否新增 StudioTeamTopologyGAgent,以及 topology fact 的最小字段词汇。

    详细说明

    我按 CLAUDE 口径把违规收敛为 4 个点:Query -> ReadModel 不能变成 UI/Host query-time stitching;EventEnvelope 只能继续作为投影传输壳,不能再包一层 Team topology envelope;Team topology 的 node/edge/status/count/lastSeen 是稳定查询语义,必须 typed;跨 actor 聚合如果是长期业务事实,必须由 aggregate actor 拥有,不能塞进 projector 重算。

    推荐拆两刀。第一刀不引入新抽象:新增 StudioTeamEventTopologyDocument 和 GET /api/scopes/{scopeId}/teams/{teamId}/event-topology,只消费 StudioTeamState committed publication,返回 Team 节点、空 edges、stateVersion/updatedAt/watermark。这刀约 +520/-20 LOC、15 个文件、5 组行为测试,主要价值是立刻禁止前端用 /api/actors/{actorId}/graph-enriched 冒充 Team topology。第二刀再做真正结构设计:如果要表达 member/run/service 间 EventEnvelope flow,新增 StudioTeamTopologyGAgent 或等价 aggregate owner,提交 typed topology facts 后再由 Projection Pipeline 物化多节点边。

    方案 取舍
    先落 first slice 边界正确、成本可控,但 topology 信息量低,主要是稳定 API contract 和 honest empty/stale state。
    直接做 aggregate actor 架构最完整,但需要 owner/lifecycle/proto 字段决策,不能由 solver 自动拍板。
    用现有 actor graph fallback 成本最低,但继续混用 runtime graph、run artifact 和 Team 长期事实,应拒绝。

    Escalation triggers: new-core-actor-type、new-proto-contract、lifecycle-topology、performance-benchmark。


    📎 完整 codex 原始输出(存档备查)
    ---
    solver: structural
    issue: 737
    cluster: phase9-issue737
    verdict: escalate
    ---
    
    ## CLAUDE clause violated (quoted verbatim)
    > 读写分离:`Command -> Event`,`Query -> ReadModel`;异步完成通过事件通知,不在会话内拼装流程。
    > 查询始终走 readmodel:对外查询只读 readmodel;不暴露 actor 内部状态、state mirror payload 或 event replay 为查询主路径。
    > `EventEnvelope` 是唯一投影传输壳:业务消息与投影消息都用 `EventEnvelope`;区别由强类型 payload 表达,禁止引入第二层包络。
    > readmodel 按需创建:只有存在稳定消费场景(明确消费方、查询入口、返回 DTO)时才新增 readmodel。
    > 聚合必须 actor 化:跨 actor 聚合/汇总/关联若有稳定业务语义,建模为 aggregate actor;禁止长期放在 query-time 拼装层。
    > projection 只消费 committed 事实:基于 committed domain event 或其同源 durable feed 构建;禁止订阅入站 command、self continuation 或 actor 运行时偶然结构。
    > 版本对齐权威源:readmodel 版本必须来自权威 actor 的 committed version 或等价水位;禁止本地 projection counter 或 `StateVersion++` 冒充权威版本。
    > 新增状态/事件/持久化载荷:先定义 `.proto` 并生成类型,再接入实现;禁止先写临时结构后补 Protobuf。
    
    ## Recommended framing
    Team `事件拓扑` 不能命名为现有 actor-rooted `graph-enriched` 的别名,也不能在 Host 或前端 query-time 拼多个 run graph;它是以 `scopeId + teamId` 为资源身份的查询契约。结构上应拆成两层:第一层先新增 Team-owned typed topology readmodel/API,只消费 `StudioTeamGAgent` 已提交 current-state facts,暴露空或最小稳定节点/边与 `stateVersion/updatedAt`,从而立刻切断 UI fallback 和 query-time stitching;第二层如要表达跨 member/run/service 的长期 EventEnvelope flow,必须由 `StudioTeamTopologyGAgent` 这类 aggregate actor 拥有事实并提交 typed topology facts,再由同一 Projection Pipeline 物化。这个设计不靠例外:查询仍只读 readmodel,投影仍只消费 committed facts,跨 actor 聚合不会塞进普通 projector 当第二套业务状态机。
    
    ## Concrete plan
    - New abstractions (if any):
      - First slice: none. Reuse `IProjectionWriteDispatcher<T>`, `IProjectionDocumentReader<T,string>`, `ICurrentStateProjectionMaterializer<StudioMaterializationContext>`, existing Application query port/service.
      - Later design slice: `StudioTeamTopologyGAgent` aggregate actor + typed `StudioTeamTopologyState` / `StudioTeamTopologyFactCommitted` proto contracts. Layer: Domain/Actor contract in `Aevatar.GAgents.StudioTeam`; projection readmodel in `Aevatar.Studio.Projection`.
    - Files:
      - `src/Aevatar.Studio.Application/Studio/Contracts/TeamContracts.cs`: add `StudioTeamEventTopologyResponse`, `StudioTeamEventTopologyNode`, `StudioTeamEventTopologyEdge`, `StudioTeamEventTopologyWatermark`.
      - `src/Aevatar.Studio.Application/Studio/Abstractions/IStudioTeamQueryPort.cs`: add `GetEventTopologyAsync(scopeId, teamId)`.
      - `src/Aevatar.Studio.Application/Studio/Abstractions/IStudioTeamService.cs`: add `GetEventTopologyAsync(scopeId, teamId)`.
      - `src/Aevatar.Studio.Application/Studio/Services/StudioTeamService.cs`: query topology via query port; if absent, verify Team summary through existing `GetAsync` and return not found honestly.
      - `src/Aevatar.Studio.Projection/ReadModels/studio_projection_readmodels.proto`: add `StudioTeamEventTopologyDocument`, `StudioTeamEventTopologyNodeEntry`, `StudioTeamEventTopologyEdgeEntry`, `StudioTeamEventTopologyWatermark`.
      - `src/Aevatar.Studio.Projection/ReadModels/StudioTeamEventTopologyDocument.Partial.cs`: implement `IProjectionReadModel<StudioTeamEventTopologyDocument>`.
      - `src/Aevatar.Studio.Projection/Metadata/StudioTeamEventTopologyDocumentMetadataProvider.cs`: document metadata provider.
      - `src/Aevatar.Studio.Projection/Projectors/StudioTeamEventTopologyProjector.cs`: consume `EventEnvelope<CommittedStateEventPublished>` unpacked as `StudioTeamState`; write an owner-scoped topology document with Team node and no cross-actor edges in first slice.
      - `src/Aevatar.Studio.Projection/QueryPorts/ProjectionStudioTeamQueryPort.cs`: map topology document to Application DTO; no event store/runtime reads.
      - `src/Aevatar.Studio.Projection/DependencyInjection/ServiceCollectionExtensions.cs`: register projector/readmodel metadata.
      - `src/Aevatar.Studio.Hosting/Endpoints/StudioTeamEndpoints.cs`: add `GET /api/scopes/{scopeId}/teams/{teamId}/event-topology` through `IStudioTeamService`.
      - `apps/aevatar-console-web/src/shared/api/models.ts` or scoped Team model file: add typed Team topology model.
      - `apps/aevatar-console-web/src/shared/api/runtimeDecoders.ts`: add Team topology decoder if current API layer centralizes decoders there.
      - `apps/aevatar-console-web/src/shared/api/studioTeamsApi.ts`: add `getTeamEventTopology(scopeId, teamId)`.
      - `apps/aevatar-console-web/src/pages/teams/detail.tsx`: consume Team endpoint only; keep no actor graph fallback.
      - Later slice only: new aggregate actor/proto/projector files after owner decision.
    - LOC delta: first slice ~+520 / -20 across 15 files; later aggregate slice ~+650 / -0 across 8-10 files.
    - Tests to add:
      - `test/Aevatar.Studio.Tests/StudioTeamEventTopologyProjectorTests.cs`: committed `StudioTeamState` creates topology readmodel with Team node, `state_version` from `StateEvent.Version`, `updated_at` from envelope timestamp, and no invented local version.
      - `test/Aevatar.Studio.Tests/ProjectionStudioTeamQueryPortTopologyTests.cs`: query maps typed topology document; missing topology does not query event store/runtime and returns null/empty according to service contract.
      - `test/Aevatar.Studio.Tests/StudioTeamServiceTopologyTests.cs`: missing Team returns `StudioTeamNotFoundException`; existing Team with not-yet-materialized topology returns a response with empty nodes/edges plus honest stale/empty status.
      - `test/Aevatar.Studio.Tests/StudioTeamEndpointTests.cs`: endpoint calls service, maps 200/404/400, and preserves scope access guard.
      - `apps/aevatar-console-web/src/pages/teams/detail.test.tsx`: Team detail calls `studioTeamsApi.getTeamEventTopology(scopeId, teamId)` and never calls `runtimeActorsApi.getActorGraphEnriched` for Team event topology.
      - Later slice tests: aggregate actor commits topology facts from explicit typed inputs; projector materializes multi-actor edges idempotently by aggregate committed version.
    - proto changes (if any):
      - `src/Aevatar.Studio.Projection/ReadModels/studio_projection_readmodels.proto`: add new messages only, no field mutation on `StudioTeamCurrentStateDocument`.
      - Suggested tags: `StudioTeamEventTopologyDocument.id = 1`, `actor_id = 2`, `state_version = 3`, `last_event_id = 4`, `updated_at = 5`, `scope_id = 20`, `team_id = 21`, `repeated nodes = 30`, `repeated edges = 31`, `watermark = 40`.
      - Later slice: new actor-owned topology fact/state proto under StudioTeam actor contracts; field numbers require owner approval.
    - Runtime cost:
      - First slice projection: +1 projector branch per Team committed state envelope; one document upsert per Team state commit; estimated +0.2-0.8 ms CPU and one document-store write per changed Team in local/provider path.
      - First slice query: one existing document read by `studio-team:{scopeId}:{teamId}`; estimated +1 store round trip, no actor hop, no replay, no graph-store traversal.
      - First slice frontend: +1 HTTP GET on Team detail, parallelizable with Team summary; expected p50 cost equals existing API round trip, no extra actor latency.
      - Later aggregate slice: at least +1 actor inbox hop per topology fact source and +1 aggregate commit; estimated +2-10 ms local actor runtime, provider-dependent distributed latency; allocations scale O(nodes + edges) per committed aggregate snapshot.
    - First slice: no-new-abstraction narrow plan: add Team topology readmodel/API backed only by `StudioTeamState` committed current-state publication, returning Team identity node plus honest empty topology until aggregate facts exist; update frontend to use that endpoint and keep actor graph fallback forbidden.
    - Later design slice: decide aggregate owner and typed topology fact vocabulary for cross-actor EventEnvelope flow, then implement `StudioTeamTopologyGAgent` or equivalent Team-owned aggregate state before materializing multi-actor edges.
    
    ## Risks
    - First slice returns a structurally correct but low-information topology. It buys boundary correctness and a stable UI contract, not full EventEnvelope flow insight.
    - Adding topology DTOs before aggregate facts can look over-engineered if the UI only needs an empty state. Keep the first slice bounded to one Team node, empty edges, version/watermark fields, and no generic graph abstraction.
    - Writing topology into `StudioTeamCurrentStateDocument` would be cheaper, but it would overload the current-state summary and invite cross-actor state machine logic inside that projector.
    - Using `IProjectionGraphStore` in the first slice would add graph provider coupling before there is graph traversal value. Document readmodel is enough until there are real multi-node edges or graph queries.
    
    ## Escalation triggers (if any)
    - ESCALATE_REASON:new-core-actor-type:cross-actor Team EventEnvelope topology needs a durable aggregate owner before multi-actor edges are business facts
    - ESCALATE_REASON:new-proto-contract:aggregate topology fact/state field vocabulary and field numbers need owner approval
    - ESCALATE_REASON:lifecycle-topology:if `StudioTeamTopologyGAgent` is introduced, creation/reuse/retire semantics must be approved
    - ESCALATE_REASON:performance-benchmark:multi-actor topology fan-in cost cannot be validated from local tests alone once edge volume grows
    
    ## Reasoning trace (internal — for meta-judge)
    - Why this structure beats a documented exception: an exception would let UI or Host stitch runtime/run graphs and normalize the violation. A typed Team readmodel plus aggregate owner keeps `Query -> ReadModel`, keeps `EventEnvelope` as the only transport shell, and gives every topology edge a committed owner/version.
    - Why I picked this abstraction over alternatives: first slice uses no new abstraction because there is only one real caller and existing projection/query seams already fit. Later aggregate actor is justified only when cross-actor topology becomes a stable business fact; projection alone cannot own that fact without becoming a second business state machine.
    - What I cannot decide alone: the exact authoritative owner and source vocabulary for real cross-actor EventEnvelope topology: Team actor direct state, a new aggregate actor, or a narrower member/service-only first aggregate.
    
    SOLVER_DONE:structural:escalate:propose Team-owned typed topology readmodel first, escalate aggregate owner for cross-actor EventEnvelope flow:first-slice=no-new-abstraction Team topology readmodel/API backed by StudioTeamState committed facts
    ⟦AI:AUTO-LOOP⟧

    ⟦AI:AUTO-LOOP⟧

  5. loning commented on May 31, 2026

    @loning
    Contributor

    🤖 Delete solver r2 — 不再建议关闭 #737

    TL;DR

    • 这是什么: delete solver 第二轮重新回答 Team-scoped event topology 是否有当前承诺。
    • 结论是什么: 没有当前代码 consumer,但 docs/canon/frontend-design.md 仍是 active canon 并承诺 Team Workbench 包含 topology,所以本轮改为 abstain。
    • controller 下一步: 不要按 orphan 直接关闭;若继续实现,第一刀必须绑定真实 Team Workbench consumer,不能先加无人读取的空壳 readmodel。

    详细说明

    我反向追了当前 surface:teamRoutes.ts 只允许 overview | members,旧 connectors/events/topology deep link 已回落到 overview;Team detail 测试明确断言没有“事件拓扑”按钮,并且 scope-only 链接不会调用 runtimeActorsApi.getActorGraphEnriched。所以从代码调用链看,Team-scoped topology 当前没有稳定 consumer。

    但这还不能证明 #737 是 orphan。docs/canon/frontend-design.md 是 status: active 的 canon 文档,Console Web 产品主语是 AI Team Workbench,并把 Team Workbench 子视图列为 activity、topology、intervention、members、team-scoped bindings。因此本轮对 meta-judge 收敛问题的答案是:当前没有代码 consumer,但有稳定产品承诺。delete solver 不应继续主张删除;如果 controller 认为这个 canon 已不再绑定,才应关闭 #737 并同步清理 canon/docs。

    方案 取舍
    直接关闭 #737 代码上安全,但会和 active canon 冲突。
    先加无人消费的 readmodel/API 会制造新的 orphan surface,不建议。
    consumer-backed first slice 保留产品承诺,同时要求 API/readmodel 与 Team Workbench UI/route 同步出现。

    📎 完整 codex 原始输出(存档备查)
    ---
    solver: delete
    issue: 737
    cluster: phase9-issue737
    verdict: abstain
    ---
    
    ## Classification
    (e) Genuinely needed by current product commitment, but with no active code consumer yet
    
    ## Recommended action
    不建议继续按上一轮 delete 结论把 #737 直接关闭为 orphan feature。反向追踪结果是:当前代码里确实没有 Team-scoped event topology 的稳定 consumer,Team detail 也已经把 `topology/events/connectors` 旧 tab 收敛到 `overview`;但 `docs/canon/frontend-design.md` 是 active canonical 文档,并明确把 `Team Workbench` 的子视图列为 `activity、topology、intervention、members、team-scoped bindings`。这回答了 meta-judge 的收敛问题:没有当前代码 consumer,但有当前稳定产品承诺。因此 delete solver 不应推动删除;后续应由 minimal/structural solver 选择最小实现路径,并且必须先绑定真实 Team Workbench UI/API 消费场景,不能只为了“以后可能用”新增空壳 readmodel。
    
    ## Concrete plan (if propose)
    - Files to delete: none.
    - Caller migrations: none.
    - Tests to delete: none。Existing tests仍然有价值:它们证明当前 Team detail 没有伪装使用 actor graph 当 Team topology。
    - LOC delta: 0.
    - First slice: none from delete solver. If implementation proceeds, the first slice must be a no-new-abstraction consumer-backed slice: add Team topology API/readmodel only together with an immediate Team Workbench consumer or explicit empty/stale UI state; do not add a readmodel that no route or UI reads.
    - Later design slice: none from delete solver.
    
    ## Reverse-evidence (why deletion is not safe enough)
    - No current code consumer exists: `rg` found no `/event-topology`, `StudioTeamEventTopology`, or `TeamEventTopology` contract in `src`, `apps`, or `test`.
    - Current frontend blocks the old fake path: `apps/aevatar-console-web/src/shared/navigation/teamRoutes.ts` only allows `overview | members`, and `teamRoutes.test.ts` maps legacy `connectors/events/topology` deep links to `overview`.
    - Current Team detail tests explicitly reject the old UI promise: `apps/aevatar-console-web/src/pages/teams/detail.test.tsx` asserts the `事件拓扑` button is absent and that scope-only Team links do not call `runtimeActorsApi.getActorGraphEnriched`.
    - Actor topology remains a separate drilldown: active callers of `runtimeActorsApi.getActorGraphEnriched` are app-level live ops, actors pages, chat advanced console, and MissionControl; those are actor/run-rooted views, not `scopeId + teamId` Team topology.
    - Product commitment is still current: `docs/canon/frontend-design.md` has `status: active`, `last_updated: 2026-05-11`, defines Console Web as `AI Team Workbench`, and lists Team Workbench subviews including `topology`.
    - Older PRDs are not enough by themselves, but they consistently explain the same gap: previous designs wanted Team event topology, while warning that existing `actorId -> graph` is only member/actor-level and not `scopeId -> full team topology`.
    - No external repo dependency is involved: NyxID / chrono-storage / chrono-ornn are untouched and not required for the decision.
    
    ## Risks
    - If `docs/canon/frontend-design.md` is no longer binding, then #737 reverts to an orphan feature and should be closed with doc cleanup. I did not find repo evidence that this canon has been superseded.
    - If implementation starts with a readmodel/API that no UI or route consumes, it will create the orphan shape the delete solver is trying to avoid.
    - If the UI reuses `/api/actors/{actorId}/graph-enriched` as a Team topology fallback, it will reintroduce query-time stitching and resource identity confusion.
    - If cross-actor Team topology is treated as long-lived business fact, minimal implementation must not let projection invent aggregate state; an aggregate owner decision belongs to structural solver.
    
    ## Escalation triggers (if any)
    - ESCALATE_REASON: product-canon-conflict if controller decides `docs/canon/frontend-design.md` is aspirational rather than binding.
    - ESCALATE_REASON: orphan-readmodel-risk if a proposed first slice adds `StudioTeamEventTopologyDocument` without a concrete Team Workbench consumer.
    - ESCALATE_REASON: aggregate-owner-required if the first real consumer needs cross-member/run/service edges rather than a Team-owned empty/stale topology state.
    
    ## Reasoning trace (internal — for meta-judge)
    - Why I do not claim this can be deleted: the old code surface is gone, but an active canon document still commits the product to Team Workbench topology. That is a stable product commitment even without a current code consumer.
    - What I checked to verify safety: `gh issue view 737 --comments`; `.refactor-loop/runs/audit-iter-832.md`; CLAUDE.md architecture philosophy; `docs/canon/frontend-design.md`; Team routes and tests; Team detail tests; Studio Team endpoint/projector/readmodel surface; frontend actor graph callers; `git log -p --follow -S 'topology'` for Team routes.
    - What I cannot decide alone: whether the active canon should be amended to remove topology from Team Workbench. If controller makes that product decision, deletion becomes clean: close #737 and update canon/docs to keep Team Workbench to overview/members only.
    
    SOLVER_DONE:delete:abstain:team-topology-has-active-canon-product-commitment-but-no-current-code-consumer:first-slice=none-delete-solver-requires-consumer-backed-no-new-abstraction-slice
    ⟦AI:AUTO-LOOP⟧
    ⟦AI:AUTO-LOOP⟧
  6. 20 remaining items

  7. loning commented on May 31, 2026

    @loning
    Contributor

    🤖 Structural solver r10 — abstain positive Team topology, keep guard slice

    TL;DR

    • 这是什么:Add Team-scoped EventEnvelope topology read model and API #737 phase9-r10 structural solver,按当前源码重新收敛 Team topology 域。
    • 结论是什么:abstain;当前生产 Team detail 只有 overview/members,没有 Team topology 消费面,直接新增 readmodel/API 会违反 readmodel 按需创建。
    • controller 下一步:先做 tests-only guard/delete slice;只有产品重新声明可见消费者和 topology owner 后,再开设计 slice。

    详细说明

    当前证据是负向的:StudioTeamCurrentStateDocument 只承载 Team summary,StudioTeamEndpoints 没有 /event-topology,Console Team detail 没有生产 getActorGraphEnriched 调用,且测试仍断言“事件流/事件拓扑”按钮不存在。runtimeActorsApi.getActorGraphEnriched(actorId) 是 actor-rooted drilldown API,不是 Team-scoped 查询契约;如果未来把它当 Team topology fallback,才会触发 Query -> ReadModel 与 runtime-shape-not-fact 违规。

    结构解法不应现在硬造一个空 topology readmodel。第一刀是无新抽象:删除/收紧 legacy tab=events/topology/connectors 测试残影和 test-only actor graph mock,保留“Team detail 不暴露事件拓扑、不调用 actor graph”边界测试,约 -90~-120 LOC、2 个测试文件。第二刀只有在明确消费者后才成立:新增 StudioTeamEventTopologyDocument + Application query port + Host endpoint;跨 member/run/service 的长期边若成为业务事实,必须先决定 aggregate owner,例如 StudioTeamTopologyGAgent,再由 Projection Pipeline 物化 typed nodes/edges。

    方案 取舍
    first slice: tests-only guard/delete 不新增抽象、不碰 proto/backend;立刻防止 actor graph 被误命名成 Team topology。
    later slice: Team topology readmodel/API 需要明确 UI/API 消费者;否则违反 readmodel 按需创建。
    later slice: aggregate owner 能表达跨 actor EventEnvelope flow,但需要 human nod 决定 owner/lifecycle/字段词汇。

    Escalation triggers 只用于后续正向设计:new-core-actor-type、new-envelope-kind、new-pipeline-phase、lifecycle-topology、cross-cluster、performance-benchmark。


    📎 完整 codex 原始输出(存档备查)
    ---
    solver: structural
    issue: 737
    cluster: phase9-r10
    verdict: abstain
    ---
    
    ## CLAUDE clause violated (quoted verbatim)
    > 删除优先:空转发、重复抽象、无业务价值代码直接删除,不保留兼容空壳。
    > readmodel 按需创建:只有存在稳定消费场景(明确消费方、查询入口、返回 DTO)时才新增 readmodel。
    > 查询始终走 readmodel:对外查询只读 readmodel;不暴露 actor 内部状态、state mirror payload 或 event replay 为查询主路径。
    > `EventEnvelope` 是唯一投影传输壳:业务消息与投影消息都用 `EventEnvelope`;区别由强类型 payload 表达,禁止引入第二层包络。
    > 聚合必须 actor 化:跨 actor 聚合/汇总/关联若有稳定业务语义,建模为 aggregate actor;禁止长期放在 query-time 拼装层。
    > projection 只消费 committed 事实:基于 committed domain event 或其同源 durable feed 构建;禁止订阅入站 command、self continuation 或 actor 运行时偶然结构。
    > 版本对齐权威源:readmodel 版本必须来自权威 actor 的 committed version 或等价水位;禁止本地 projection counter 或 `StateVersion++` 冒充权威版本。
    
    ## Recommended framing
    当前 repo 里 #737 不是一个已经存在的 Team topology 违规实现,而是一个尚未落地的正向能力请求;生产 Team detail 已收敛到 `overview/members`,没有可见 topology tab,也没有生产 `runtimeActorsApi.getActorGraphEnriched` fallback。结构上不能为了“未来可能要事件拓扑”先新增空 readmodel/API,因为 `readmodel 按需创建` 要求稳定消费场景。正确收敛是先删除或收紧 legacy Team topology 测试残影,把边界锁成“Team 页面不得把 actor-rooted graph 命名为 Team topology”;等产品重新声明 Team topology 的 UI/API 消费者后,再按 Projection Pipeline + typed readmodel 设计。如果跨 member/run/service 的 EventEnvelope flow 要成为长期事实,必须由 Team-owned aggregate actor 提交事实,不能在 Host、QueryPort 或普通 projector 里 query-time stitching。
    
    ## Concrete plan
    - New abstractions (if any): none for the valid first slice. Later only if a stable consumer exists: `StudioTeamEventTopologyQuery` as Application read contract is not a new seam, just `IStudioTeamQueryPort` extension; `StudioTeamTopologyGAgent` aggregate actor is a new Domain actor and needs explicit owner/lifecycle approval.
    - Files:
      - `apps/aevatar-console-web/src/shared/navigation/teamRoutes.test.ts`: delete legacy named `connectors/events/topology` deep-link test; generic unknown-tab fallback already covers behavior.
      - `apps/aevatar-console-web/src/pages/teams/detail.test.tsx`: remove test-only `runtimeActorsApi.getActorGraphEnriched` import/mock/reset/assertion and legacy `tab=events` narrative; keep or strengthen assertions that “事件流” and “事件拓扑” controls are absent.
      - No production files in first slice.
      - Later, if consumer is approved: `src/Aevatar.Studio.Application/Studio/Contracts/TeamContracts.cs`, `IStudioTeamQueryPort.cs`, `IStudioTeamService.cs`, `StudioTeamService.cs`, `studio_projection_readmodels.proto`, `StudioTeamEventTopologyDocument.Partial.cs`, `StudioTeamEventTopologyDocumentMetadataProvider.cs`, `StudioTeamEventTopologyProjector.cs`, `ProjectionStudioTeamQueryPort.cs`, `ServiceCollectionExtensions.cs`, `StudioTeamEndpoints.cs`, frontend `shared/studio` API/model files.
    - LOC delta: first slice ~-90 to -120 / +0 across 2 test files. Later readmodel/API slice ~+520 to +650 / -0 across 11-14 files. Later aggregate-owner slice ~+650 to +900 / -0 across 7-10 files.
    - Tests to add:
      - First slice: no new test file; keep `detail.test.tsx` absence assertion and add one explicit negative assertion only if needed: Team detail does not call `runtimeActorsApi.getActorGraphEnriched` from production code without mocking the API as a Team dependency.
      - Later readmodel/API slice: `StudioTeamEventTopologyProjectorTests` asserts committed `StudioTeamState` writes only Team-root topology with authority `state_version`; `StudioTeamQueryPortTopologyTests` asserts readmodel-only query and no runtime/event-store use; `StudioTeamEndpointTests` asserts `/event-topology` scope guard and 200/404/400 mapping; frontend API decoder test asserts URL and DTO shape.
      - Later aggregate slice: aggregate actor behavior tests assert typed topology facts are committed idempotently and projector materializes edges by aggregate committed version, not local counters.
    - proto changes (if any): first slice none. Later readmodel/API slice adds `StudioTeamEventTopologyDocument`, `StudioTeamEventTopologyNode`, `StudioTeamEventTopologyEdge`, `StudioTeamEventTopologyWatermark` to `src/Aevatar.Studio.Projection/ReadModels/studio_projection_readmodels.proto`; suggested fields `id=1`, `actor_id=2`, `state_version=3`, `last_event_id=4`, `updated_at=5`, `scope_id=20`, `team_id=21`, `watermark=30`, `nodes=40`, `edges=41`. Aggregate slice needs separate actor-owned proto after owner decision.
    - Runtime cost: first slice 0 runtime cost. Later readmodel/API slice: +1 projector branch for Studio Team committed-state envelopes, +1 document upsert per Team commit, +1 document read per topology query; estimated local +0.2-0.8 ms CPU per Team commit and one store round trip per query. Later aggregate slice: at least +1 actor inbox hop and +1 aggregate commit per topology fact source; local estimate +2-10 ms before provider/network overhead, allocations O(nodes + edges) per aggregate snapshot.
    - First slice: no-new-abstraction narrow plan: tests-only deletion/guard slice that removes legacy Team topology residues and locks Team detail away from actor graph fallback.
    - Later design slice: only after a stable consumer is declared, add Team-scoped typed topology readmodel/API; only after cross-actor flow facts are declared stable, decide and implement `StudioTeamTopologyGAgent` or equivalent aggregate owner.
    
    ## Risks
    - Abstaining from positive readmodel/API may disappoint a product expectation hidden outside this repo; current repo has no route, DTO, production caller, or visible tab proving that expectation.
    - Tests-only first slice does not deliver topology data; it only prevents the wrong replacement path.
    - Later `StudioTeamTopologyGAgent` can be over-engineering if the only requested view is a Team root node. Keep it gated to concrete cross-actor edges and at least one visible consumer plus the API contract.
    
    ## Escalation triggers (if any)
    - ESCALATE_REASON:new-core-actor-type:cross-actor Team topology facts need a unique aggregate owner such as StudioTeamTopologyGAgent.
    - ESCALATE_REASON:new-envelope-kind:if the design proposes anything besides existing EventEnvelope payload contracts.
    - ESCALATE_REASON:new-pipeline-phase:if Team topology requires a Projection phase beyond current materializers/projectors.
    - ESCALATE_REASON:lifecycle-topology:if existing StudioTeamGAgent lifecycle/topology must change to own flow facts.
    - ESCALATE_REASON:cross-cluster:if another in-flight Team Workbench/frontend PR reintroduces the topology tab or API consumer.
    - ESCALATE_REASON:performance-benchmark:if expected node/edge volume needs graph-store production benchmark before choosing document vs graph surface.
    
    ## Reasoning trace (internal — for meta-judge)
    - Why this structure beats a documented exception: it does not rely on “don’t misuse actor graph” prose; the first slice removes the stale association, and the later slice requires a typed Team contract before any UI can consume topology.
    - Why I picked this abstraction over alternatives: no abstraction is justified today because there is no active consumer; `IStudioTeamQueryPort` extension is enough once an API consumer exists, and an aggregate actor is only justified when cross-actor topology becomes stable business fact.
    - What I cannot decide alone: whether product wants to restore a visible Team topology surface, which actor owns cross-actor flow facts, and whether the API should be document-readmodel-only or also backed by graph store.

    SOLVER_DONE:structural:abstain:no-active-team-topology-consumer-so-positive-readmodel-api-would-violate-readmodel-on-demand:first-slice=tests-only-delete-legacy-tabs-and-actor-graph-mock

    
    </details>
    ⟦AI:AUTO-LOOP⟧
    
  8. loning commented on May 31, 2026

    @loning
    Contributor

    ✅ split — #737 r10 拆为 #1541(first)+ #1542(later)。⟦AI:AUTO-LOOP⟧

  9. loning commented on May 31, 2026

    @loning
    Contributor

    🤖 meta-reflector r4 决议 — #737

    结论:retry-fix。r4 的 escalate:philosophy 属于 over-escalate;真正命中 hard trigger 的是 later topology design,不是可拆的 first-slice。

    1. team-topology-binding 本身不必触及 canon vocabulary。只有当 solver 要改 docs/canon/*、决定 Team Workbench topology 的绑定/非绑定身份,或新增 topology fact 词汇时,才进入 canon 决策。
    2. first-slice boundary 不需要 maintainer architecture decision,只要收窄到 existing code surface:Console Team route/detail/tests,不新增 backend topology readmodel/API/proto/projector,不新增 actor type/envelope kind/pipeline phase。
    3. r3 已经 narrow 到 split 后 r4 再升级,说明 solver 仍把三层混在一起:existing UI surface guardrail、backend typed topology readmodel/API、canon/product commitment。这个混淆不等于必须 human decision。
    4. 最小可解决议是重派 narrowed round:r5 必须放下 canon,只 propose 已有 code surface 改动;backend topology facts、aggregate owner、watermark/version、document vs graph authority 都留 later design-pending。

    本地 artifact:.refactor-loop/runs/meta-reflect-issue737-r4.md

    META_RESOLVED:retry-fix:r5-existing-code-surface-only-no-canon-no-backend-topology-readmodel
    ⟦AI:AUTO-LOOP⟧

  10. loning commented on May 31, 2026

    @loning
    Contributor

    🤖 Structural solver r5 — 只收 existing Console surface

    TL;DR


    详细说明

    当前可落地的问题不是生产 Team topology 实现错误,而是测试 surface 仍把已删除的 events/topology/connectors tab 和 actor-rooted getActorGraphEnriched(actorId) mock 绑在 Team detail 叙事里。生产 TeamDetailTab 只有 overview | members,detail.tsx 不导入 runtimeActorsApi,仓库内也没有 /event-topology / TeamEventTopology / StudioTeamEventTopology 符号。

    所以本轮 structural 解法必须很窄:删除 teamRoutes.test.ts 里的 legacy tab 参数化用例,删除 detail.test.tsx 里的 test-only runtimeActorsApi mock/reset/not-called 断言和 tab=events deep-link 用例;保留现有“事件流/事件拓扑按钮不存在”的行为断言。这样能把现有代码面锁成“Team detail 不把 actor graph 当 Team topology fallback”,同时不会违反 readmodel 按需创建。

    切片 改动 成本 取舍
    first slice 只改 2 个前端测试文件 约 -95 到 -110 LOC,0 runtime cost 清掉误导 surface,不交付 topology 数据
    backend design 本轮不提 0 LOC 留给 #1542,有明确消费者后再设计
    canon/product 本轮不提 0 LOC 遵守 r4,避免把词汇/产品绑定混进 first slice

    📎 完整 codex 原始输出(存档备查)
    ---
    solver: structural
    issue: 737
    cluster: phase9-r5
    verdict: propose
    ---
    
    ## CLAUDE clause violated (quoted verbatim)
    > 删除优先:空转发、重复抽象、无业务价值代码直接删除,不保留兼容空壳。
    > readmodel 按需创建:只有存在稳定消费场景(明确消费方、查询入口、返回 DTO)时才新增 readmodel。
    > 查询始终走 readmodel:对外查询只读 readmodel;不暴露 actor 内部状态、state mirror payload 或 event replay 为查询主路径。
    > 运行时形态不是业务事实:不得把本地实例类型、代理类型、对象可见结构当成业务绑定依据。
    
    ## Recommended framing
    r5 的可收敛违规不是“缺一个 Team topology 后端”,而是现有 Console 测试 surface 还保留两个会误导实现方向的残影:legacy `events/topology/connectors` tab 被当成有名字的兼容入口测试,`TeamDetailPage` 测试里 mock 了 actor-rooted `runtimeActorsApi.getActorGraphEnriched(actorId)` 并断言 scope-only 页面不调用它。生产代码已经没有 Team topology tab,也没有 Team detail 对 actor graph API 的调用;继续保留这些测试残影,会让后续实现误以为可以在 Team 页面沿用 actor graph fallback。结构上只需要删除这两个残影,把现有代码面锁成“Team detail 只支持 overview/members,actor graph 仍属于 actor/run drilldown”,不新增任何 backend topology/readmodel/proto/projector,也不触碰 canon 词汇。
    
    ## Concrete plan
    - New abstractions (if any): none.
    - Files:
      - `apps/aevatar-console-web/src/shared/navigation/teamRoutes.test.ts`: 删除 `it.each(["connectors", "events", "topology"])` legacy named deep-link 测试;保留现有 `tab=not-real` generic fallback 测试,覆盖未知 tab 回到 `overview` 的行为。
      - `apps/aevatar-console-web/src/pages/teams/detail.test.tsx`: 删除 test-only `runtimeActorsApi` import、`jest.mock("@/shared/api/runtimeActorsApi")` block、`beforeEach` 里的 `getActorGraphEnriched.mockClear()`、scope-only no-call assertion。
      - `apps/aevatar-console-web/src/pages/teams/detail.test.tsx`: 删除 `falls legacy event deep links back to the overview tab` 用例;它把 `tab=events` 和 Team detail 运行数据加载绑在一起,但生产 route parser 已把未知 tab 统一落到 overview,且 canonical fallback 已由 `teamRoutes.test.ts` 覆盖。
      - `apps/aevatar-console-web/src/pages/teams/detail.test.tsx`: 保留 `renders the chinese team-first overview shell` 中 `事件流`、`事件拓扑` button absent 断言,作为现有 surface 的边界测试。
    - LOC delta: ~+0 / -95 到 -110。
    - Tests to add: 0 个新测试文件;调整 2 个现有测试文件。保留的行为覆盖是:canonical unknown tab fallback、Team detail 不暴露事件流/事件拓扑按钮、scope-only link 不启动 detail data flows。
    - proto changes (if any): none.
    - Runtime cost: 0 latency hops、0 backend allocations、0 HTTP/API changes;只减少 Jest mock/setup 体量,测试启动/执行成本预计微降但不作为目标。
    - First slice: no-new-abstraction narrow plan: tests-only cleanup of legacy Team topology tabs and test-only actor graph mock residue.
    - Later design slice: none in this r5 proposal. #1542 可在明确 UI/API 消费者后单独讨论,但本轮不提出 backend topology readmodel、aggregate owner、watermark/version、document vs graph authority 或 canon vocabulary 改动。
    
    ## Risks
    - 删除 `tab=events` 测试会移除一个旧 deep-link 叙事;现有 parser 对未知 tab 已有 generic fallback 测试,风险是隐藏 off-repo 链接不再被具名记录。
    - first slice 不交付 Team topology 数据;它只防止错误的 actor graph fallback 叙事继续留在测试面。
    - 如果后续 #1542 恢复可见 Team topology tab,本切片保留的“事件拓扑按钮不存在”断言需要由那个实现 PR 按新消费者契约更新。
    
    ## Escalation triggers (if any)
    - ESCALATE_REASON:docs-canon-change:任何修改 `docs/canon/*` 或决定 Team topology 产品绑定的方案不属于 r5 first slice。
    - ESCALATE_REASON:new-backend-topology-readmodel:任何新增 Team topology API/proto/projector/readmodel 的方案不属于 r5 first slice。
    - ESCALATE_REASON:new-core-actor-type:任何新增 aggregate owner 或改变 StudioTeam actor lifecycle/topology 的方案需要单独设计。
    - ESCALATE_REASON:cross-cluster:如果 #1541/#1542 或其他 in-flight PR 已经恢复 Team topology consumer,需要重新对齐切片边界。
    
    ## Reasoning trace (internal — for meta-judge)
    - Why this structure beats a documented exception: 不靠文档说“不要用 actor graph fallback”,而是把测试里唯一把 Team detail 与 actor graph/topology tab 叙事相连的残影删掉。
    - Why I picked this abstraction over alternatives: 没有新 abstraction;当前有 0 个生产 Team topology caller,新增 port/readmodel 会违反 `readmodel 按需创建`,而现有 route parser 与页面测试 surface 已足够表达 first slice。
    - What I cannot decide alone: 是否以及何时恢复可见 Team topology consumer;如果恢复,权威 facts、API DTO、水位与物化形态应由 #1542 单独裁决。

    SOLVER_DONE:structural:propose:existing-code-surface-tests-only-cleanup:first-slice=delete-legacy-team-topology-tabs-and-test-only-actor-graph-mock

    ⟦AI:AUTO-LOOP⟧
  11. loning commented on May 31, 2026

    @loning
    Contributor

    🤖 Delete solver r5 — 只删 Console Team 测试残影

    TL;DR

    • 这是什么:Add Team-scoped EventEnvelope topology read model and API #737 phase9-r5 delete solver,按 r4 决议只看 existing code surface。
    • 结论是什么:propose;生产 Team detail 已无 topology/event tab,也没有 Team 调 graph-enriched,只剩测试残影可删。
    • controller 下一步:执行 first slice,删除 2 个前端测试文件里的 legacy tab 与 test-only actor graph mock,不新增后端 readmodel/API/proto。

    详细说明

    反查链路后,TeamDetailTab 现在只有 overview | members,apps/aevatar-console-web/src/pages/teams/detail.tsx 没有导入 runtimeActorsApi,getActorGraphEnriched 的生产 caller 都在 actor/run drilldown surface(Actors、MissionControl、chat/app)。/api/actors/{actorId}/graph-enriched 是以 actor 为 root 的运行时图查询,不是 Team-scoped topology;本轮不能把它包装成 Team topology,也不能新增 Team topology readmodel/API。

    可删的是测试残影,不是生产功能:teamRoutes.test.ts 仍把 connectors/events/topology 作为 legacy deep link 名称逐一保存;detail.test.tsx 仍有未被生产页使用的 runtimeActorsApi mock/reset/not-called 断言,以及 tab=events legacy deep-link 叙事。git log -S topology 显示生产 tab 在 2026-05-19 的 Team surface 重构中删除;这些测试只是在保存旧叙事,不保护当前可见能力。

    方案 取舍
    tests-only 删除/收紧 约 -80~-120 LOC,0 生产运行时成本,锁住“Team 不用 actor graph 冒充 topology”。
    关闭 #737 主诉求 本轮不建议;r4 已要求只收敛 first-slice,不裁决 canon/product commitment。
    新增 Team topology readmodel/API 本轮禁止;会越过 r5 的 existing code surface boundary。

    📎 完整 codex 原始输出(存档备查)
    ---
    solver: delete
    issue: 737
    cluster: phase9-r5
    verdict: propose
    ---
    
    ## Classification
    (b) Orphan feature
    
    ## Recommended action
    建议只删除 Console Team 现有代码面上的旧 topology/event 测试残影,不改生产代码,不关闭 #737 的主设计问题,也不新增 backend topology readmodel/API/proto/projector。当前生产 Team 详情页已经收敛到 `overview | members`,没有 Team topology tab,没有 `/event-topology` DTO/route,也没有从 Team detail 调用 actor-rooted `runtimeActorsApi.getActorGraphEnriched`。因此删除视角能安全提出的 first slice 是:清理 legacy `connectors/events/topology` tab 测试和 test-only actor graph mock,保留生产页不暴露“事件流/事件拓扑”的负向断言。
    
    ## Concrete plan (if propose)
    - Files to delete: none.
    - Caller migrations: none. `runtimeActorsApi.getActorGraphEnriched(actorId)` 继续保留给 actor/run drilldown surfaces;Team detail 没有生产 caller。
    - Tests to delete:
      - `apps/aevatar-console-web/src/shared/navigation/teamRoutes.test.ts`: 删除 `it.each(["connectors", "events", "topology"])` legacy named-tab 参数化测试;现有 `tab=not-real` fallback 测试已经覆盖 unknown tab -> overview 的通用行为。
      - `apps/aevatar-console-web/src/pages/teams/detail.test.tsx`: 删除 `runtimeActorsApi` import、`jest.mock("@/shared/api/runtimeActorsApi")`、`beforeEach` 里的 `getActorGraphEnriched.mockClear()`、no-team-selected 测试里的 `expect(runtimeActorsApi.getActorGraphEnriched).not.toHaveBeenCalled()`。
      - `apps/aevatar-console-web/src/pages/teams/detail.test.tsx`: 删除或改写 `falls legacy event deep links back to the overview tab`,避免继续保存 `tab=events` 作为 Team detail 叙事;保留“事件流/事件拓扑按钮不存在”的 overview shell 断言。
    - LOC delta: about -80 to -120, deletion-positive; production LOC delta 0.
    - First slice: no-new-abstraction tests-only deletion/guard slice on Console Team route/detail tests.
    - Later design slice: none in this solver. 本轮不提出 canon vocabulary、backend topology、new readmodel、team-binding 抽象或 aggregate owner。
    
    ## Reverse-evidence (why this is safe to delete)
    - No public API breaks: `rg` found no `/event-topology`, `TeamEventTopology`, or `StudioTeamEventTopology` route/DTO/proto in `src`, `apps`, or `test`.
    - No production Team caller: `apps/aevatar-console-web/src/pages/teams/detail.tsx` does not import `runtimeActorsApi`; `getActorGraphEnriched` callers are `apps/aevatar-console-web/src/pages/actors`, `MissionControl`, `chat`, and app-level actor drilldown paths.
    - Production Team tabs are already collapsed: `apps/aevatar-console-web/src/shared/navigation/teamRoutes.ts` defines `TeamDetailTab` as only `overview | members`; `parseTeamTab` treats everything else as fallback.
    - Git history supports residue classification: `git log -S topology -- apps/aevatar-console-web/src/shared/navigation/teamRoutes.ts` shows `topology/events` were removed from the production tab union and parser on 2026-05-19.
    - `git log -S getActorGraphEnriched -- apps/aevatar-console-web/src/pages/teams/detail.test.tsx` shows the Team test mock was introduced for a no-team-selected data-flow guard, but current production Team detail has no corresponding dependency.
    - Tests covering the path are not load-bearing for a Team topology implementation: they assert legacy fallback or absence/mocking behavior, not a current endpoint, typed DTO, projection, or UI consumer.
    - No external repo dependency: NyxID / chrono-storage / chrono-ornn are untouched and not needed for this first slice.
    
    ## Risks
    - If an off-repo bookmark relies on preserving the exact `tab=events` test narrative, deleting that test removes a compatibility assertion; current production parser already treats that value as unknown and falls back.
    - If a future positive Team topology slice restores a visible tab, it will need its own typed Team API/readmodel tests; this first slice neither helps nor blocks that.
    - If controller wants a stronger guard that Team detail never calls actor graph, deleting the mock removes a direct not-called assertion. I still prefer deletion because importing the API in the Team test creates the false impression that Team detail depends on actor graph; a future implementation should be caught by production import review or a targeted boundary test.
    
    ## Escalation triggers (if any)
    - none for this first slice. Escalation would only trigger if the change expands into production Team topology API/readmodel/proto, canon vocabulary, backend topology facts, aggregate owner, or team-binding abstraction, all explicitly out of scope for r5.
    
    ## Reasoning trace (internal — for meta-judge)
    - Why I claim this can be deleted: the remaining topology/event references in Team surface are test-only residues after the production tab union and UI were removed; they do not protect a live Team topology capability.
    - What I checked to verify safety: `gh issue view 737 --comments`; `.refactor-loop/runs/audit-iter-348.md`; `CLAUDE.md` deletion-first and readmodel-on-demand clauses; `teamRoutes.ts`; `teamRoutes.test.ts`; `detail.test.tsx`; `rg` for `getActorGraphEnriched`, `/event-topology`, `TeamEventTopology`, `StudioTeamEventTopology`, and `topology`; `git log -S topology`; `git log -S getActorGraphEnriched`; `git log -S tab=events`.
    - What I cannot decide alone: whether later product/design still wants Team-scoped topology; r5 explicitly forbids deciding that via canon/backend changes, so this solver only proposes the existing-code first slice.

    SOLVER_DONE:delete:propose:tests-only-remove-team-legacy-topology-tab-and-actor-graph-mock-residue:first-slice=delete-console-team-test-residue-no-new-abstraction
    ⟦AI:AUTO-LOOP⟧

    
    </details>
    ⟦AI:AUTO-LOOP⟧
    
  12. added 2 commits that reference this issue on Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions