Skip to content

[refactor-design] iter18 cluster-006-command-path-projection-lifecycle #732

@loning

Description

@loning
id: cluster-006-command-path-projection-lifecycle
rule_ids: [R-WRITE-OBSERVE-001, R-PROJECTION-ACTOR-001]
severity: medium
requires_design: true
files_touched_estimate: 8-20
old_pattern: "Command/application services call EnsureProjection/ActivateProjection inline before or around writes."
new_pattern: "Write path dispatches command/event only; projection activation is explicit setup, actor-owned binding, or background materializer outside command request stack."

Evidence:

  • src/workflow/Aevatar.Workflow.Infrastructure/Runs/WorkflowRunActorPort.cs:75-76: activates binding and materialization during run creation.
  • src/platform/Aevatar.GAgentService.Application/Services/ServiceCommandApplicationService.cs:47 and later: command methods ensure read projections.
  • src/platform/Aevatar.GAgentService.Governance.Application/Services/ServiceGovernanceCommandApplicationService.cs:34: governance command primes projection.
  • src/platform/Aevatar.GAgentService.Infrastructure/Adapters/LlmSessionRegistrationAdapter.cs:49: registration adapter ensures projection after creating actor.

Fix boundary:

  • Separate command/write lifecycle from projection activation.
  • Keep projection startup services or explicit activation APIs if they are outside query/write request paths.
  • Add code comment in command services: New pattern: command path emits facts; readmodel visibility is observed asynchronously.

human_brief:
problem_title: "写路径顺手启动读模型投影"
problem_statement: |
多个 command/application service 在创建或修改 actor 时,会同步调用 EnsureProjectionActivateAsync。这让一次写请求同时承担写侧命令和读侧物化准备,调用方很容易误以为返回后 readmodel 已经可见。开发者会关心这个问题,因为这会模糊 ACK 语义,也让 query/readmodel 的生命周期散落在各个业务入口。
problem_example_file_path: "src/workflow/Aevatar.Workflow.Infrastructure/Runs/WorkflowRunActorPort.cs:67-82"
problem_example_code: |
runActor = await _runtime.CreateAsync(
BuildRunActorId(definitionResolution.ActorId),
ct: ct);
createdActorIds.Add(runActor.Id);
if (!string.IsNullOrWhiteSpace(definitionResolution.ActorId))
await _runtime.LinkAsync(definitionResolution.ActorId, runActor.Id, ct);
await EnsureBindingProjectionAsync(runActor.Id, ct); // ← problem: 写路径同步启动 binding projection
await EnsureExecutionMaterializationAsync(runActor.Id, ct); // ← problem: 写路径同步启动 materialization

await _dispatchPort.DispatchAsync(
    runActor.Id,
    CreateWorkflowRunBindEnvelope(
        definitionResolution.ActorId,
        runActor.Id,
        definition.WorkflowYaml,

why_needs_design: |
需要决定 projection activation 是资源创建的一部分、后台 materializer 的职责,还是由显式 lease/binder API 管理。不同能力现在各自调用 ensure,统一迁移会影响 ACK 文案、readmodel freshness、启动服务和测试契约。
design_question: "哪些 projection 必须在 actor 创建前预绑定,哪些可以完全交给后台 materializer?请给出统一生命周期规则。"
original_authors:
- "@eanzhao"
- "@loning"
- "@auric"

📢 cc 原作者

决策需要(进 Phase 9 design 共识)

本 issue 由 iter18 audit 标 requires_design=true 自动开。Auto-loop 已加 phase9-auto-solve label,3 solver(minimal/structural/delete)+ meta-judge 自动启动,等 3/3 unanimous 即派 implement。

Maintainer 可任意评论参与;每条评论触发 fresh solver round。

🤖 Auto-loop iter18 / codex-refactor-loop

Metadata

Metadata

Assignees

No one assigned

    Labels

    auto-loopCreated by codex-refactor-loop skillauto-loop-resumeSet when design decision is ready; codex-refactor-loop will resume implementphase9-auto-solveOperator opted this design issue into Phase 9 auto-solverefactor-design-neededCluster flagged requires_design by codex-refactor-loop auto audit🎉 phase:merged

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions