Skip to content

Design needed: iter38 cluster-038-studio-scoped-workflow-drafts-storage-authority #847

@loning

Description

@loning

Cluster

id: cluster-038-studio-scoped-workflow-drafts-storage-authority
rule_ids:

  • R-AUTHORITATIVE-OWNER
  • R-CQRS-SEPARATION
  • R-QUERY-READMODEL-ONLY
  • R-DIP-LAYERING
    severity: high
    requires_design: true
    files_touched_estimate: 5-10
    old_pattern: "Scoped workflow drafts are declared authoritative editor state but saved and queried directly through a ChronoStorage blob adapter."
    new_pattern: "Scoped workflow drafts are actor-owned workspace facts; storage is a provider behind actor commit/projection, and queries read the materialized draft readmodel."

Evidence:

- `src/Aevatar.Studio.Application/Studio/Abstractions/IWorkflowDraftStore.cs:5-10`: interface comment says drafts are authoritative editor state.
- `src/Aevatar.Studio.Infrastructure/DependencyInjection/ServiceCollectionExtensions.cs:59`: authoritative draft port is bound to `ChronoStorageWorkflowDraftStore`.
- `src/Aevatar.Studio.Infrastructure/Storage/ChronoStorageWorkflowDraftStore.cs:20-40`: save writes `ScopedWorkflowDraftFact` directly to blob storage.
- `src/Aevatar.Studio.Infrastructure/Storage/ChronoStorageWorkflowDraftStore.cs:71-91`: get reads protobuf bytes directly from blob storage and returns the draft.
- `src/Aevatar.Studio.Application/AppScopedWorkflowService.cs:136-150`: application service reads existing draft and saves through the store in the write path.
- `src/Aevatar.Studio.Application/AppScopedWorkflowService.cs:399-406`: list path groups direct store results into query response.
- `src/Aevatar.Studio.Application/AppScopedWorkflowService.cs:432`: get path reads direct store result.
- Contrast path: `src/Aevatar.Studio.Application/Studio/Services/WorkspaceService.cs` and `src/Aevatar.Studio.Infrastructure/ActorBacked/ActorDispatchStudioWorkspaceCommandPort.cs` show actor-backed workspace draft command flow already exists for the non-scoped path.

Fix boundary:

- Do not modify ChronoStorage or require a new external endpoint; `../chrono-storage` was not present and AGENTS forbids relying on external product changes.
- Keep protobuf payloads if storage remains an implementation detail; serialization format is not the violation.
- Move authoritative scoped draft writes behind an actor-owned workspace/scope command contract, then expose query results via readmodel/projection.
- Preserve current Studio editor UX and draft DTOs; cluster is about authority and read/write boundary, not adding editor features.

human_brief:
  problem_title: "Studio scoped draft 把对象存储当成了业务真相"
  problem_statement: |
    Scoped workflow draft 被接口注释定义为“编辑器里的权威草稿状态”,但真正的保存和读取直接落到 ChronoStorage blob。这样业务事实没有 actor 拥有,也没有通过 command/event/readmodel 链路进入系统的统一状态模型。以后要做版本、并发、回滚、权限审计或多节点一致性时,这条路径会绕过已有的 workspace actor 规则。
  problem_example_file_path: "src/Aevatar.Studio.Infrastructure/Storage/ChronoStorageWorkflowDraftStore.cs:20-41"
  problem_example_code: |
    public async Task SaveDraftAsync(
        string scopeId,
        string workflowId,
        string workflowName,
        string yaml,
        WorkflowLayoutDocument? layout,
        CancellationToken ct)
    {
        var normalizedWorkflowId = NormalizeRequired(workflowId, nameof(workflowId));
        var context = ResolveWorkflowContext(scopeId, $"{WorkflowDirectory}/{normalizedWorkflowId}.yaml");
        if (context == null)
            throw new InvalidOperationException("Scoped workflow draft storage is not enabled.");

        var fact = new ScopedWorkflowDraftFact // ← problem: 业务事实在应用侧直接组装为存储 payload
        {
            WorkflowId = normalizedWorkflowId,
            WorkflowName = workflowName,
            Yaml = yaml,
            Layout = layout is null ? null : ToProtoLayout(layout),
        };
        await _blobClient.UploadAsync(context, fact.ToByteArray(), "application/x-protobuf", ct); // ← problem: 保存直接写 blob,而不是 actor command/event
    }
  why_needs_design: |
    修复需要决定 scoped draft 是否复用现有 Studio workspace actor,还是新建 scope-workspace actor。还要决定草稿 readmodel 的版本、水位和与 publish flow 的关系;这些都是公共业务契约,不适合用机械替换完成。
  design_question: "Scoped workflow draft 应该归现有 workspace actor 管,还是为每个 scope 建一个独立 draft/workspace actor?"
  original_authors:
    - "@loning"
    - "@AbigailDeng"
    - "@eanzhao"
    - "@potter-sun"

```yaml

## 待 maintainer 决策
请评论或加 `auto-loop-resume` label。不评论则 auto-loop 在 phase9-auto-solve label 下走 3 solver consensus。

📢 cc 原作者: @loning

⟦AI:AUTO-LOOP⟧

Metadata

Metadata

Assignees

No one assigned

    Labels

    auto-loopCreated by codex-refactor-loop skillphase9-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