Skip to content

feat(core): bind @-workflow confirms to a real WorkflowRun of the chosen snapshot - #71

Merged
EricWvi merged 27 commits into
ora-space:mainfrom
creegon2:feat/158-workflow-snapshot-confirm
Oct 21, 2026
Merged

EricWvi merged 27 commits into
ora-space:mainfrom
creegon2:feat/158-workflow-snapshot-confirm

Conversation

@creegon2

@creegon2 creegon2 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Refs ora-space/circus#374

交接:本 PR 的后续开发按 circus#158 的交接改由 @Zpc001 在接续运行 circus#374 推进;circus#158 是此前的运行,提交、审查与测试历史都在本 PR。

Issue 里 @ Workflow 点 Confirm 时,按所选发布版本创建唯一的 WorkflowRun,并与 IssueRun 稳定关联;直接启动、manual workflow IssueRun 和 @ 确认走同一个创建函数;调度器推进 WorkflowRun 时,状态同步投影回关联的 IssueRun。已适配 main 上的运行身份与真实会话执行。circus#70 已关闭,fixtures=false 的真实 Node 联合验收仍未交付,补做时与 circus#77 的验收范围协调。

改动

  • Confirm / manual 建 WorkflowRun 并关联(issue_interactions.go、issue_runs.go、workflow_invocation.go):同一事务里依次做准入(admitWorkflowRun)→ insertWorkflowRun 建运行(snapshot_id 取准入解析出的版本,input 只放 Start 变量和全局变量)→ 冻结调用者的执行器槽位绑定 → 入队 IssueRun 并写 workflow_invocation_ref → 原有的 run_id IS NULL 比较交换。三个入口的共用创建函数统一冻结 created_by、project_id、plugin_version。Confirm/manual 的创建人取已验证的调用者,Project 取 Issue 的 project_ref;直接启动取请求体 projectId。三种入口均要求合法可访问的 Project,缺失返回 409 workflow_run_project_required。Confirm/manual 不通过开发模拟器伪造执行或跳过槽位绑定。
  • 已关联的 workflow IssueRun 不再交给 legacy Dispatcher(issue_run_lifecycle.go),避免同一次调用出现两份互不相关的执行结果。TestWorkflowConfirmValidatesAndExecutes 相应改为断言关联和 WorkflowRun 行。
  • 迁移 0052_workflow_invocation_reference.sql:先把没有引用、仍未终结的历史 workflow IssueRun 结算为 failed(workflow_execution_unavailable_before_invocation_linkage),再加 (workflow_invocation_ref, tenant_id) → workflow_runs(id, tenant_id) 复合外键、引用唯一、执行器类型与引用一致的 CHECK(历史终态行放行),无条件 VALIDATE。编号接在 main 已有的 0051_workflow_run_cancel.sql 之后;该列此前没有写入者,迁移注释里写明。
  • 状态投影 projectWorkflowRunToIssueRun(workflow_issue_projection.go):在调用方事务里把 WorkflowRun 状态映射到 IssueRun(pending→queued、running→running、succeeded→completed、failed→failed、cancelled→cancelled),写 run.started / run.message / 终态 system activity,不冒充 workflow 评论;awaiting_input 不改 IssueRun。
  • 投影接入调度器(workflow_scheduler.go、workflow_run_session.go、workflow_run.go):认领 pending→running 的比较交换成功后(同一 UPDATE 已写 started_at)调用投影;settleWorkflowRunStates 改为返回是否更新到行,只有更新到行才投影,终态结算同一 UPDATE 先写 finished_at。会话完成、失败、启动超时、取消都经过这个结算。摘要由调用方 workflowIssueOutputSummary 从输出节点结果或运行错误生成,按 UTF-8 字符边界截到 ≤20000 字节,超长输出不会让结算回滚;只有契约冲突(执行器不一致、改写已终结的 IssueRun)才使整个事务回滚。行为变化:结算没有更新到行时,finishWorkflowRun 不再做 Workspace 清理,会话节点不再在本轮继续推进下游,由下一轮按已提交状态重算。
  • pending 时间戳:本分支的 9e0fcb2(pending WorkflowRun 详情的空 startedAt / finishedAt 返回 null)与 main 上 cloud#105 的 9e1e4fb 修的是同一件事,合并后相对 main 不再产生 diff;保留的只有 integration/workflow_run_timestamp_test.go。
  • main 合并适配:保留上游 0043–0051 迁移,将调用引用迁移顺延为 0052。执行器槽位路由遗漏 SlotName 的问题已由 cloud#94 在 main 上修复(带 HTTP 覆盖),本分支的 router.go 与 main 一致,不再单独携带这处修改。创建函数与已审过的冻结语义保持原实现。
  • 前端:@ 表单切换版本时按 GET …/snapshots/:snapshotId/form 重取该版本的表单;一次编辑只建一个 interaction,Confirm 失败重试复用它;已确认的行显示 WorkflowRun 状态并链接到运行详情,没有关联的历史行回退显示 IssueRun 状态;只有选项全是本 workflow 已发布快照时才把 snapshotId 字段当平台版本选择器(作者自己声明的同名字段不受影响);workflow_version_fields_changed 显示为可操作的提示。
  • 文档:docs/collaboration/interactions、docs/workflows/workflows(中英)、internal/core 与迁移目录中英 README。

测试

  • integration/workflow_invocation_test.go:未发布、跨 workflow / 跨租户 snapshot、非法输入都不留下任何行;选旧版本后编辑并重新发布,运行的 snapshot / input / definition 不变;同 Idempotency-Key 重放、换 key 409、并发两个 Confirm 只建一个 WorkflowRun 和一个 IssueRun;manual 路径同样关联;IssueRun.input.workflowSnapshotId 与 workflow_runs.snapshot_id 一致。
  • integration/workflow_invocation_migration_test.go:空库与带历史数据升级两种情况下约束都已 VALIDATE;四种非终态结算、三种终态保留、迁移重放、历史行之后仍可 UPDATE、新的无引用 workflow IssueRun 被拒。
  • integration/workflow_invocation_identity_test.go:Confirm/manual 冻结实际调用者、Issue Project、插件版本与逐槽位模型绑定;后续配置变化不改写冻结值;开发模拟器不能使这两个入口跳过绑定或写出模拟执行结果;缺 Project 时两类运行、活动与交互认领均回滚。
  • 调度器侧(internal/core/workflow_scheduler_db_test.go、integration/workflow_invocation_test.go,真实 PostgreSQL):TestWorkflowConfirmSchedulerProjectsRunningOnce(认领后 IssueRun running、started_at 与 WorkflowRun 一致、只有一条 run.started)、TestWorkflowConfirmSchedulerCompletesAndAllowsNextInvocation(完成后 IssueRun completed、completed_at 等于 finished_at,同一 Issue 可再次 Confirm)、TestWorkflowConfirmSchedulerProjectsConditionFailure、TestWorkflowAdvanceProjectsConcurrentPassesOnce、TestWorkflowSettleLostCASDoesNotProject、TestWorkflowSettleTruncatesIssueSummary、TestWorkflowAdvanceProjectionFailureRollsBackClaim;直接运行仍不产生 Issue 活动。
  • internal/core/workflow_issue_projection_test.go:状态映射、活动顺序、从 queued 直达终态补 run.started、awaiting_input、重放。
  • integration/workflow_run_timestamp_test.go:pending 运行详情的空时间戳。
  • 插件导入副本(circus#73 移交,复用 cloud#77 的真实 .orax + HTTPS + PostgreSQL 工具,不 seed 数据):
    • TestImportedWorkflowDirectoryAndRunsIgnoreImportTriggerUser:导入副本出现在 @ 选择器目录,所选快照表单取自导入快照;换管理员、停用导入触发者后,目录、表单与运行结果不变。
    • TestImportedWorkflowSnapshotBindsConfirmAndDirectRuns:@ Confirm 与直接运行选导入快照,WorkflowRun 都绑定该 snapshot_id;改活稿后再建运行仍冻结导入快照的图与输入。
    • TestImportedWorkflowRemainsPublishableAndRunnableAfterUninstall:卸载插件后副本仍可读、旧快照逐字节不变,可以发布下一版本并用它建出运行。
    • 以上只证明运行记录的创建与快照绑定,不证明 Node 执行。
  • 前端组件 / API 测试:版本切换重取表单、单 interaction、重试复用、状态与详情链接、历史行回退。
  • 再合并 main 三次(a302856:cloud#91/feat(core): record agent run cancellation requests #100/feat(core): declare and resolve workflow globals and sys.* variables #104/fix(core): keep unset workflow run timestamps null #105;8d1f3c7:cloud#83/feat(model-access): add personal Agent configurations and frozen run inputs #101/feat(model-gateway): support pinned Claude Messages requests #102/fix(core): omit the agent declaration association for undeclared plugins #107;08102c2:cloud#108/feat(core): cancel workflow runs by request and settle them at the scheduler #109)。cloud#101 占用 0050、cloud#109 占用 0051,调用引用迁移顺延为 0052_workflow_invocation_reference.sql,文件内容不变;迁移 README 按 0050、0051、0052 排序。main 新增的结算路径——Node 不可达(cloud#91)、单轮超时(cloud#108)、开始前取消和运行中取消(cloud#109)——都经 finishWorkflowRun → settleWorkflowRunStates 结算,同样投影到关联 IssueRun;开始前取消投影为 queued → cancelled,不补写 run.started。advanceWorkflowRun 新增的不可达窗口参数,在投影回滚测试里按生产传入 store.DeliveryUnreachableAfter。验证:go build ./...、go vet、go run ./cmd/checkformat、task lint(0 issues)通过;前端完整 npm run check 通过(138 个文件、1160 个用例);带真实 PostgreSQL,internal/core 的 Workflow/IssueRun 用例全过,integration 的 Migration/Workflow 用例 292 个通过,其中只有 TestWorkflowAssetImportDoesNotFanOutToWorkspaces 在机器高负载时报模拟 Node node_not_ready,单独重跑通过;它来自 main 的 cloud#83,本 PR 不涉及。上一轮合并(a302856)在真实 PostgreSQL 上跑完的 core 全量通过。
  • 再合并 main(140849f:cloud#95/fix(core): 工作流会话在初始回合结束后由调度器结束 #96/feat(core): expose WorkflowRun model attempt statistics reads #98/fix: pass the desktop default agent config through the publish gate #99,迁移顺延 0050;main 新增的会话结束超时经 finishWorkflowRun 结算,同样投影到关联 IssueRun):checkformat、task lint(0 issues)通过;前端完整 npm run check 通过;真实 PostgreSQL 上 internal/core 通过,integration 537 通过、5 跳过,失败的只有两个已知 Windows 专属 Git server 用例。credentialmaterial、credentialstore、gateway、gitpreset、gitserver、repoexec 这几个包在 Windows 上测 Unix 文件权限、符号链接和 git 服务,本地失败;本 PR 对这些包没有任何改动,与 main 一致,以 CI 为准。
  • 再合并 main(ebb93cd,迁移顺延 0049;enqueueRun 保留 main 的任务会话准入检查,enqueueAdmittedRun 与它都经 enqueueRunWithInvocation 写入,前端 IssueRun 类型同时保留 workflowInvocationRef 与 main 的 triggerEvidenceRefId):go build、checkformat、task lint(0 issues)通过;前端完整 npm run check 通过(0 clones、构建成功);真实 PostgreSQL 上 core + integration 1133 通过、5 跳过,失败的是两个已知 Windows 专属 Git server 用例,以及与本 PR 无关的插件计划用例 TestStartPluginPlanSurvivesSelectionChangesAndExecutionFailures/unknown(与前端安装同时运行、机器负载高时超时,单独连跑 4 次均通过)。
  • 本版(f833135:投影接线 713b191、fb34d82,再合并 main 迁移顺延 0046):go build ./...、go run ./cmd/checkformat、task lint(0 issues)通过;带真实 PostgreSQL 跑 go test ./internal/core/... ./integration:1083 通过、5 跳过、2 失败,失败的仍是已知 Windows 专属的两个 Git server 用例(git-http-backend.exe 检测),与本改动无关。
  • 本次合并 main(1ae5e42,迁移顺延 0045):go build ./...、go run ./cmd/checkformat、task lint(0 issues)通过;带真实 PostgreSQL 跑 go test ./internal/core/... ./integration:1064 通过、5 跳过、2 失败,失败的是已知 Windows 专属的 TestGitServerAuthorizesExecutorSyncFromCloudState、TestGitServerAuthorizesDeliveryPushFromCloudState(git-http-backend.exe 检测),与本改动无关。
  • 本次 external-git-line 合并(Windows、真实 PostgreSQL,REQUIRE_POSTGRES=1):10 个顶层定向用例已有通过结果,0 跳过,覆盖新 0044 的空库/升级、direct 创建、Confirm/manual 身份冻结、缺 Project 回滚与并发 Confirm。首次 9 个顶层用例通过,身份冻结用例暴露路由遗漏;补回 SlotName 后,仅重跑该用例的 confirm/manual 两个子例并通过,复用其余 9 项结果。合并代码的全仓 checkformat/lint 通过;恢复字段后只检查该路由文件格式和 router 包 lint,均通过。新提交的完整 CI 另行报告。
  • 上一版 889a84d(Windows、真实 PostgreSQL,REQUIRE_POSTGRES=1):聚焦 go test ./internal/core ./integration 通过,无失败或跳过;覆盖共享创建、Confirm/manual、导入快照、迁移与新增身份/Project 回滚场景。另对身份冻结、缺 Project 回滚、并发 Confirm 执行定向 -race,全部通过;go run ./cmd/checkformat、task lint 通过;该 head 的 CI check/frontend 均成功。这些结果仅对应旧版本,不代替此次 external-git-line 合并后的验证。
  • 合并前的历史验证:go build ./...、./internal/core 全过;Windows 全量 integration 曾因上游 Git 服务测试的 git-http-backend.exe 检测失败。前端触及文件的格式、lint、typecheck、测试与 frontend:check:diff 当时通过;这些历史结果不代替本次合并后的完整 CI。

ADR 状态

  • 实现的 ADR:ora-space/specs 的 decisions/cloud/workflow/0-cloud-side-scheduler-owns-workflow-execution.md(approved)D3 中的创建与引用部分:三种入口共用启动函数;创建事务里冻结 created_by / project_id / plugin_version / 逐槽位绑定;workflow_invocation_ref 的租户一致复合外键、唯一引用与 CHECK;存量非终态 workflow IssueRun 的一次性结算。
  • 合并后未全部实现:fixtures=false 的真实 Node 联合验收仍未交付。ADR 保持 approved,剩余部分由 circus#158 的接续运行承接(circus#70 已关闭),本次不开把 ADR 改为 implemented 的 specs PR。
  • 不涉及:decisions/cloud/workflow/20261015-executor-slots-replace-agent-model-ids.md(approved)的槽位语义本次未改,只在创建时按既有语义冻结绑定;decisions/cloud/workflow/20261016-asset-import-and-published-snapshots.md 已是 implemented,本次只补导入快照经 @ Confirm 与直接运行的调用证据。

未包含 / 待对齐

  • Project 是用户可见的创建要求:没有 Project 的 Issue 不能再跑 workflow;编辑器的项目选择器由 cloud#70 负责。已有的三种入口共用 Project 校验,缺失时不留下无法执行的运行。
  • 字段与活稿不同的旧版本仍被拒:前端按所选版本渲染表单,但 Confirm 仍按 feat(core): publish-time graph validation and one workflow start gate #65 的规则比较活稿与所选版本,字段不一致时返回 409 workflow_version_fields_changed(前端显示为"先发布活稿或另选版本")。所以 feat(core): bind @-workflow confirms to a real WorkflowRun of the chosen snapshot #71 验收"选旧 snapshot 用旧 schema"在字段不同的情形下做不到;这条门禁规则不在 specs 里,是否放宽由 owner 决定。
  • fixtures=false 部署上的真实 Agent 节点验收:circus#70 已关闭,仍未补做;补做时与 circus#77 的验收范围协调,不重复建设。

🤖 Generated with Claude Code

Pending WorkflowRun details must retain JSON null for startedAt and finishedAt instead of invalid empty date-time strings. TestPendingWorkflowRunDetailPreservesNullTimestamps directly checks both fields over HTTP. Build and full core/integration validation are logged per commit; the known Windows Git integration failure remains unchanged.
Create and link WorkflowRun/IssueRun atomically for Confirm and manual starts, bypass legacy dispatch, and enforce tenant-scoped one-to-one references in migration 0036. Settle all unlinked non-terminal history, retain editable terminal rows, and validate constraints on every database. Preserve the admission base live-form divergence gate verbatim. HTTP, concurrency, snapshot freeze and fresh/seeded migration regressions accompany this change; full core/integration and build checks are logged at this commit.
Project authoritative state in the caller transaction, write system activities and summaries, and preserve rollback/idempotency. Supplement run.started before direct terminal outcomes only with actual start evidence; awaiting_input leaves the IssueRun unchanged without rejecting the source transaction. PostgreSQL regressions cover these paths. Scheduler wiring remains deferred; full core/integration and build checks are logged at this commit.
Keep one interaction through confirm retries, show authoritative WorkflowRun status and link to its detail page. Historical rows without an invocation reference retain IssueRun status and have no run link.

When the user selects a published version, load its snapshot form with issue context, render that version's fields, discard obsolete answers and retain the selected snapshot instead of the descriptor's newest-version default. Component/API regressions and bilingual module docs accompany the feature. The existing backend field-divergence gate stays unchanged; no polling or backend endpoint is added.

The final rebased tree is validated with Go build, full core/integration packages and focused frontend gates. Race, per-commit rebuilds and HTTP/browser rechecks are explicitly excluded from this rebase follow-up.

@Zpc001 Zpc001 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve

006078ca6b667c2ce28fe4050bd0cd3421f5cfa9(基于含 #65 的 df507f0,无冲突,CI check / frontend 两项通过)。事务、迁移、投影三块我逐条核过,没有要改的地方;下面 5 条是接线与记录上的义务,建议合并前落到 PR 正文或对应 PR 里。

核过之后确认没问题的部分

  • Confirm 的原子性:同一事务里 admitWorkflowRun → insertWorkflowRun → enqueueRunWithInvocation(ref 与 IssueRun 同一条 INSERT)→ interaction 的 run_id IS NULL 比较交换;insertWorkflowRun 先于比较交换,所以 CAS 失败时 WorkflowRun 随事务一起回滚,不会留下无人引用的执行。TestWorkflowConfirmAdmissionRollsBackBothRuns 把 issue_runs / workflow_runs / issue_activities / interactions 四样一起断言,TestWorkflowConfirmConcurrentInvocationCreatesExactlyOnePair 断言 200+409 且各一张行,正好覆盖这条。
  • 迁移顺序:先结算再上约束(否则 CHECK 加不上);UNIQUE (workflow_invocation_ref) 对非 workflow 行无影响(PG 允许多个 NULL);复合外键带 tenant_id,租户一致性由 SQL 保证,与 ADR「一个 workflow IssueRun 恰好引用一个 WorkflowRun,一个 WorkflowRun 至多被一个 workflow IssueRun 引用」一致。deferred 也在结算集合里是合理的——它只存在于 0010_issue_collaboration.sql:59 的 CHECK 与 OpenAPI 枚举,internal/core 里没有任何写入者。升级测试用 row_to_json 逐行比对终态行与 agent 行未变,比我预期的严格。
  • 投影函数:映射到既有状态词汇;awaiting_input 既不写 IssueRun 也不回滚调用方事务(TestWorkflowIssueProjectionAwaitingInputPreservesIssueRunAndCommits 用 commitWitness 证明调用方的更新确实提交了);终态不可回退、不可改判;重放幂等(状态相同即早返回,整行与 activity 序列都比过);run.started 的补写只在有 started_at 证据时发生,取消先于启动不伪造开始;run.message 复用既有活动词汇,且 issue_comments 计数为 0,不冒充 workflow 评论;活动写入失败会把调用方的 workflow 状态一起回滚(TestWorkflowIssueProjectionActivityFailureRollsBack)。投影与状态转换同生共死,这是对的形状。
  • 执行输入的切分是等价变换:requireWorkflowRunInput 只检查图声明的键、对未声明的键不做判定(internal/core/workflow_admission.go:61),所以 manual 路径把校验对象从整份 input 换成过滤后的 input 不改变判定;@ 路径的 values 已被 validateFormValues(..., true) 拒过未知键,过滤只去掉平台注入的 repository / branch / prompt / context_refs。TestWorkflowConfirmFreezesHistoricalSchemaAndInput 断言 workflow_runs.input 恰好是 Start 变量加全局变量,TestWorkflowConfirmValidatesAndExecutes 断言 IssueRun 侧仍保留 interactionValues.repository,两边都有痕。
  • 不必重生成契约:workflowInvocationRef 本来就在 internal/contract/openapi.go:148/:175 的 IssueRun 定义里,api/openapi.json 与 frontend/src/api/generated.schemas.ts 已含该字段且本 PR 未改它们,前端用的是生成客户端。workflowRunSummary 的 null 时间戳修复也确认了(startedAt / finishedAt 现在是原值直传,pending 运行返回 null)。
  • 前端:useWorkflowSubmission 把 interaction 的创建移进 mutation,失败时保留 interactionId 只解锁,一次编辑只有一个 interaction;历史行没有 ref 时 useWorkflowRun 的 enabled 为假,不会给每行发请求。

1(记录义务,非本 PR 引入)「按所选版本渲染表单」与 #65 的字段发散门禁互相抵消

confirmInteraction 仍然比较活稿描述符与所选版本描述符(workflowFieldDivergence,internal/core/workflow_collaboration.go:303,调用点在 issue_interactions.go 的 confirmInteraction)。前端这次改成渲染所选版本的字段,于是当所选版本的字段集合与活稿不同时,用户会看到该版本自己的表单、填好、确认,然后拿到 409 workflow_version_fields_changed——一个看得到却交不上去的表单。

这不是本 PR 引入的拒绝(改动前同样的选择也会被拒),生产行为没有回归;但本 PR 自己的 TestWorkflowConfirmFreezesHistoricalSchemaAndInput 也绕开了这个情形(注释原文"A newer graph with the same fields keeps the live-form compatibility gate satisfied"),也就是说这条路径没有证据。建议:把这条限制写进 PR 的"未包含 / 待对齐"与 #71 的验收说明,并明确跟进归属(这条门禁的规则目前不在 specs 里);若能顺手让前端把该 409 渲染成可操作的文案("该版本字段与活稿不一致,请先发布或另选版本"),比直接显示 fault 好。

2(小)isSnapshotSelector 用 key + type 判定,作者同名 select 会被误判

后端在作者自己声明 snapshotId 时不注入平台版本字段(workflow_collaboration.go:601-606),所以描述符里那个 snapshotId 字段是作者的。前端按 key === 'snapshotId' && type === 'select' 认定版本选择器,作者把它声明成 select(合法文档)时,前端会拿它的值去请求 GET …/snapshots/<作者的值>/form,404 后整个 composer 落到"表单不可用 + 重试"。测试里那一例是 text-input,所以没暴露。判定条件建议再加一层平台特征(例如选项值属于该 workflow 的快照列表),或由后端给平台字段一个稳定标记。

3(合并顺序)这份 PR 之后,没有人推进已确认的 workflow IssueRun

dispatchRun 对带 ref 的行直接返回(internal/core/issue_run_lifecycle.go:122-126),投影未接调度器,TestWorkflowConfirmValidatesAndExecutes 的期望从 run.enqueued/started/progress/message/completed 收成只剩 run.enqueued。这与 ADR「模拟 trace 不作为执行证据」一致,生产行为也不变(生产本来没有 dispatcher),作为 preflight 可以接受;但投影的调用点还没有归属:调度器在 #67 的 workflow_scheduler.go,而 #68 与 #72 也都在改这个文件。请在 #158 里写明"投影接进认领与结算"落在哪一份 PR,别让它随三份 PR 一起漂。

4(接线时)投影对时间戳的要求要传给调度器

projectWorkflowRunToIssueRun 在 running 时要求 started_at 非空、终态要求 finished_at 非空,否则 409 并把调用方的状态转换一起回滚。fail-closed 是好的选择,但它意味着调度器每次结算都必须先写时间戳,否则会陷入"结算被回滚 → 下一轮再来"。接线时请把这条写进调度器侧的测试。

5(记录义务)迁移遇到悬空引用会直接失败

复合外键是直接 ADD,若历史上存在指向不存在 WorkflowRun 的 workflow_invocation_ref,升级会失败。该列至今没有写入者(ADR 也是这么写的),现实中没有这种行,fail-closed 优于静默丢弃;建议在迁移注释里点一句"该列此前无写入者"。

Refs ora-space/circus#158

🤖 Generated with Claude Code

creegon2 and others added 7 commits October 16, 2026 17:43
Move invocation linkage to migration 0037, reserving 0035 and 0036 for the earlier identity and execution work. Explain why the composite foreign key fails closed on unexpected dangling references.

Identify the platform snapshot selector using all published snapshot ids from the existing paginated endpoint, preserving an author's same-named select as ordinary input when its options are custom. Explain how to resolve workflow_version_fields_changed in the composer without changing admission policy.

Validation: Go build, the core package, all migration and invocation integration cases with a 30-minute timeout, and touched-file frontend format, lint, types, component/API and module gates. No scheduler or identity changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two field-divergence tests repeated the same 409 handler, render and
confirm steps, which the jscpd duplication gate rejects.

Docs-Unchanged: test-only refactor; no behavior or module contract changed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep both README sections: main's session dashboard / model request
statistics and this branch's workflow invocation linkage; list the 0035
model-attempt migration before 0037 in the migrations README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reuse the real .orax, HTTPS marketplace and PostgreSQL harness to prove imported workflows enter the Issue target directory and their selected snapshot forms remain stable across administrators and import-trigger deactivation. Compare pending run bindings across those identities.

Verify Confirm links its IssueRun to a WorkflowRun bound to the imported snapshot, direct starts select the same snapshot, and editing the draft cannot replace the frozen graph. After provider uninstall, preserve the old snapshot, publish a new version and create a run from it.

Evidence stops at WorkflowRun creation and binding; real node execution remains with ora-space#70. No import implementation or specs changes.

Validation: go build ./..., the core package, three new import invocation cases and 23 existing invocation/confirm/assets regressions with 30-minute timeouts. The merge has no overlap with this branch's frontend edits, so the conditional frontend gate does not apply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… linkage

Keep this branch's WorkflowRun linkage behaviour while adopting main's
"Issue collaboration D9" citations in place of the removed migration
notes, and list main's 0036 asset-import migration before 0037.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
creegon2 and others added 2 commits October 17, 2026 18:26
…inkage

main rebuilt docs/ and removed the accreted docs tree, so the invocation
linkage notes move into docs/collaboration/interactions and
docs/workflows/workflows. main also gained 0037_repository_bindings, and
the persistence guide now requires a new migration to follow the latest
number on main, so the invocation reference migration becomes 0038.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main gained 0038_agent_workspace_blocked_deadline, so the invocation
reference migration moves to 0039 to keep following the latest number
on main; the migration READMEs list main's 0038 before it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@EricWvi EricWvi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main 已合入 cloud#85:docs/ 按领域整体重建为中英双语,文档地图在 docs/README.md,旧的 docs/*.md 已经全部删除。同时 cloud#62 已合入迁移 0038_agent_workspace_blocked_deadline.sql。请先合并或 rebase 最新 main,再做以下适配:

  • 迁移编号:main 上最新的迁移已经是 0038。请把本 PR 新增的迁移顺延到合并时 main 最新编号之后,同步改测试里的文件名、测试名和升级基线,并在 internal/core/migrations/README(.en).md 末尾追加对应小节(两个文件现在和 main 冲突)。(本 PR 新增 0038_workflow_invocation_reference)。
  • 文档已经改在新的 docs/collaboration/ 和 docs/workflows/ 下,很好;rebase 之后请确认改动没有被冲突覆盖。

@creegon2

Copy link
Copy Markdown
Contributor Author

@EricWvi 谢谢,已在 ecd2589 处理(合并最新 main 4eae91d,未 rebase):

  • 迁移编号:顺延为 0039_workflow_invocation_reference.sql,接在 main 的 0038_agent_workspace_blocked_deadline.sql 之后。测试里的文件名常量 workflowInvocationMigration 已同步;三个测试名(TestWorkflowInvocationMigration{FreshConstraints,UpgradePreservesHistory,UpgradeWithoutLegacyValidates})本身不带编号;升级基线用 migrationsBefore(workflowInvocationMigration) 计算,即应用到 main 的 0038 为止再升级。
  • README:internal/core/migrations/README(.en).md 冲突已解决,main 的 0038 小节在前,本 PR 的 0039 小节追加在末尾。
  • 文档:相对 main,本 PR 在 docs/ 下只改 docs/collaboration/interactions(.en).md 和 docs/workflows/workflows(.en).md,合并后核对过,改动都在。

本地:go build、checkformat、task lint、internal/core 测试通过;PostgreSQL 上整个 integration 包只有 3 个已知的 Windows 专属失败(Git server 的 git-http-backend 检测、Gateway 前端产物),与本改动无关,以 CI 为准。

Issue 协作文档仍把确认前的表单描述为始终取自当前文档,与前端选择已发布版本后重取快照表单的行为不符。
同步修正中英文说明,区分初次打开与选择快照后的表单来源,明确现有活稿字段差异 409 检查仍然生效,并在流程图显示关联创建的两类运行。

验证:对照 WorkflowInteractionComposer、useWorkflowSnapshotForm 和 confirmInteraction 的现有实现;两份文档的相对文件链接均有效;git diff --check 通过。
仅修改文档,不改变 API、字段差异门禁、迁移或调度行为。
@creegon2

Copy link
Copy Markdown
Contributor Author

@EricWvi 按本次 review 复核并补齐了文档,当前 head 为 52314da27b6b093e96a0213b0cfff9795da237fa,请复审。

  • 最新 upstream main 4eae91df12de2b2647ed9c7035b290199db7ee6e 已包含在分支里。调用引用迁移为 0039_workflow_invocation_reference.sql,在 main 的 0038 之后;测试常量、按 migrationsBefore(workflowInvocationMigration) 计算的升级基线,以及中英文迁移 README 末节均一致。依赖合入后、最终合并前仍按当时 main 的编号重新核对。
  • docs/ 的改动均位于重建后的 docs/collaboration/interactions(.en).md 和 docs/workflows/workflows(.en).md,没有恢复已删除的旧文档。
  • 补充修正了先前仍不准确的一段:前端初次读取活稿表单,选择平台提供的发布版本后会重取所选快照的表单;后端仍比较活稿与所选版本的字段,即使渲染了快照表单也可能返回 409 workflow_version_fields_changed。中英文同时修正,并在流程图显示 Confirm 同时创建 WorkflowRun 与关联的 IssueRun。

本次只改两份文档。核对了现有前端和 Confirm 实现、相对文件链接,git diff --check 通过;没有为文档改动重跑 Go 或前端全量测试。上一 head 的 Backend/Frontend CI 均通过,新 head 的 CI 以本轮结果为准。

本 PR 仍为 preflight:共用创建函数的身份/Project 与调度器状态同步等待 cloud#68/#72;fixtures=false 的真实 Node 联合验收等待 circus#70。这里请求复审本次文档与迁移适配,不表示这些剩余功能或总事项已完成。

合并 c6cccaf,适配已合入的运行身份、执行器槽位与真实会话执行。
direct、Confirm 与 manual 共用创建函数;后两者冻结实际调用者、Issue Project、插件版本和执行器绑定,缺 Project 时事务完整回滚,不使用开发模拟器伪造执行。
将尚未合入的调用引用迁移顺延到 0043,保留 main 的 0039–0042,并同步中英迁移与运行说明。

验证:真实 PostgreSQL 聚焦 core/integration 测试通过,无跳过;身份冻结、缺 Project 回滚和并发 Confirm 定向 race 通过;checkformat、task lint、diff --check 通过。
本次不包含调度器向 IssueRun 的状态投影调用和真实 Node 联合验收,PR 仍按 preflight 范围交付。

@Zpc001 Zpc001 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve

889a84d0426c4d5cd702031e2cc6ed9e59622279(基于含 #68/#72 的 main c6cccaf)。

  • 迁移编号 0043 正确:接在 main 已有的 0042 之后,internal/core/migrations/ 目录 max=42,无冲突;中英 README 的 0043 小节两版一致。
  • 确认链路:confirmInteraction → enqueueWorkflowInvocation → insertWorkflowRun(冻结 created_by/project_id/plugin_version)→ freezeWorkflowRunExecutors → enqueueRunWithInvocation 写 workflow_invocation_ref。workflowInvocationInput 只取 Start 变量 + 全局变量,freezeWorkflowSnapshot 把 workflowSnapshotId 写进 IssueRun.input。三入口共用创建函数,缺 Project 409 回滚。
  • 投影:projectWorkflowRunToIssueRun 在调用方事务内做状态映射 + 终态守卫,awaiting_input 不改 IssueRun;dispatchRun 跳过已关联的 workflow IssueRun,避免双份执行。
  • 迁移 SQL:先结算历史无引用非终态行,再加租户一致复合外键 + 引用唯一 + 形状 CHECK,无条件 VALIDATE。
  • 文档中英同步:docs/collaboration/interactions、docs/workflows/workflows、internal/core 与迁移目录的中英 README 均成对更新。
  • CI(check + frontend)全绿。

非阻塞观察:投影尚未接入调度器(preflight 已知,PR 正文已写明),已确认的 workflow IssueRun 停在 queued;preflight 仍等 #70 的项目选择器。

保留既有提交历史,合并 upstream/main 的 ceb39bc 外部 Git 线路,解决中英文迁移 README 冲突。
保留已合入的 0043_external_git_line,将尚未合入的 WorkflowRun 调用引用迁移顺延为 0044,并同步升级测试常量与中英说明。
新主线没有改变 direct、Confirm、manual 的共用创建函数及 created_by/project_id/plugin_version 冻结语义;本次不改运行创建实现。
定向测试发现新主线路由漏传 SlotName,使具体槽位 PUT 被当成列表请求返回 405;恢复该字段,由已有身份冻结测试直接覆盖。

验证:真实 PostgreSQL 定向测试中9个顶层用例初次通过;恢复SlotName后仅重跑剩余身份冻结用例,confirm/manual均通过,0跳过。合并代码checkformat/lint通过,字段修复后路由文件格式及router包lint通过。已通过的其余用例未重复运行。
调度器向 IssueRun 投影调用、fixtures=false 真实 Node 联合验收仍未交付,本 PR 保持 preflight。

@wanglongan587 wanglongan587 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审阅 commit:3914c49615e421a2bfd03f40c8179572a3aae3f1(base upstream/main ceb39bc,合并主线未改写历史)。CI 按此 head 复核:check pass(10m25s)、frontend pass。

按 @Zpc001 在 circus#158 分给我的四项(迁移编号与中英 README、共用创建函数的 project_id 语义、docs/ 中英成对与链接、PR 只装交付物且 preflight 未完成项如实列出)我逐项核过,这四项本身没有要改的地方。下面一条是按 circus 协议必须补的说明,另两条是建议。

需要补(阻塞)

PR 描述没有写 ADR 状态。 这个 PR 实现的是 specs decisions/cloud/workflow/0-cloud-side-scheduler-owns-workflow-execution.md(当前 approved)的 D3:三种入口共用启动函数、创建事务里冻结 created_by / project_id / plugin_version / 逐槽位绑定、workflow_invocation_ref 的租户一致复合外键 + 唯一引用 + CHECK、存量非终态 workflow IssueRun 的一次性结算——都是 D3 正文里逐条写明的义务。按 circus 的「ADR 状态跟着实现走」,产品 PR 描述要列出它实现的 ADR 路径,并逐份写明合并后是否已全部实现。请补一段,例如:

  • 实现的 ADR:decisions/cloud/workflow/0-cloud-side-scheduler-owns-workflow-execution.md(D3 的创建与引用部分),仓库 ora-space/specs;
  • 未全部实现:调度器把 WorkflowRun 状态投影回 IssueRun 的调用点、fixtures=false 的真实 Node 联合验收仍未交付,ADR 保持 approved,剩余部分由 circus#158 的后续提交 / circus#70 承接,本次不需要开把 ADR 改为 implemented 的 specs PR;
  • 不涉及的也写一句:20261015-executor-slots-replace-agent-model-ids 的槽位语义本次未改,20261016-asset-import-and-published-snapshots 已是 implemented,本次只补导入快照走 @ / 直接运行的调用证据。

描述补完重新打 status:in-review 即可,我在新 head 上确认描述并复核四项目标后给结论。

建议(不阻塞)

1. SlotName 补的是主线缺陷,建议单独落。 我核了 upstream/main:internal/core/public.go:30-31 已声明 SlotName,但 internal/api/router/router.go 构造 PublicRequest 时没有赋值,于是 personalSlotMappingsPublic 走 require(r.Method == "GET", 405, "method_not_allowed") 分支——主线现在 PUT / DELETE /api/v1/me/executor-slot-mappings/:slot 是 405,用户设置执行器槽位是坏的。这里的补法正确,证据就是新增身份冻结用例里那次 HTTP PUT(经真实 router,不是直接调 core)。但它和 #158 的主题无关,而本 PR 是 preflight、合并要等 circus#70 关闭:建议另开一个只带这一行 + HTTP 覆盖的小 PR 先修主线,或者至少开一个 circus issue 记录这个缺陷,别让主线的修复挂在一个可能几周后才合并的 preflight PR 上。至少请 owner 知道主线当前是坏的。

2. projectWorkflowRunToIssueRun 接线时留意它的 require 会回滚调度器的状态推进。 函数里 require(len(message) <= 20000, 400, ...) 与 require(w != nil, 404, ...) 失败都会让 WorkflowRun 那一次结算整体回滚,即:摘要超长(生产输出不受这个上限约束)或运行的 WorkflowRun 被软删,会让运行永远结算不了。接线时建议由调用方截断/清洗摘要,只有真正的契约冲突才 fail-closed。现在没有调用点,不阻塞。

核过、没有问题的部分

  • 迁移编号:head 里 0035–0043 与 upstream/main 逐字相同,新增只有 0044_workflow_invocation_reference.sql,无同号;中英文迁移 README 的 0044 小节一一对应(结算范围含 running / deferred、租户一致复合外键、唯一引用、其他 executor 不得携带引用、无条件 VALIDATE、证据指向 integration/workflow_invocation_migration_test.go)。合并前再按当时 main 核一次编号即可,现在没有冲突。迁移本身我按 workflow_issue_projection.go 的投影和 enqueueRunWithInvocation 的写入点了对:issue_runs_workflow_invocation_shape 要求的「非终态必须有引用、其他 executor 不得有引用」与唯一 INSERT 入口一致,没有第二条会产生无引用 workflow IssueRun 的路径(modeForType("workflow") 是 form,Task Mode 分支到不了)。
  • project_id 语义:insertWorkflowRun 对三个入口统一调 resolveWorkflowRunProject;直接运行取 r.Body.S("projectId")(路由白名单已在 main 上含 projectId),Confirm/manual 在 enqueueWorkflowInvocation 里取 issue.project_ref,因此确认人必须能打开该 Project 的 Space。空值 409 workflow_run_project_required(沿用 cloud#68 的原因码,不新造),不存在/跨租户/归档按既有 404 口径,deleting 409 resource_unavailable,冻结的是行 id 而不是入参字符串。cloud#70 的项目选择器依赖的正是这条语义,两边一致。身份冻结用例覆盖 Confirm/manual 的 created_by / project_id / plugin_version / 槽位绑定,以及缺 Project 时 IssueRun、WorkflowRun、活动、interaction 认领的完整回滚。
  • docs/:docs/collaboration/interactions、docs/workflows/workflows、internal/core/README、internal/core/migrations/README 与两份前端模块 README 都是中英成对、内容对应;我按 head 的树逐个解析了改动文档里的相对链接,全部存在,#form-mode工作流 / #端口issue-只拥有消费侧的契约 及其英文锚点都在。没有新增文档,docs/README.md 的地图不需要改。文档没有把未实现的投影写成已实现(internal/core/README 与 docs/workflows 都写明调用点未接、IssueRun 仍 queued)。
  • 交付物:37 个改动文件全是代码、测试、docs/ 与各模块 README,没有计划、进度或实现日志;git diff --check 干净。preflight 未完成项在「未包含 / 待对齐」里都如实写了(投影接线、Project 成为用户可见的创建要求、所选版本与活稿字段不一致仍被拒、fixtures=false 的真实 Node 验收)。

…nto invocation linkage

main gained 0044_model_attempt_workflow_run, so the invocation reference
migration moves to 0045; the migration READMEs list main's 0044 before it.
cloud#94 fixed the missing SlotName on main, so router.go now matches main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@wanglongan587 wanglongan587 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

结论:本次增量通过,暂不 Approve——等投影接线提交

审的 commit:1ae5e42967e77962b8cd87e9f1818114cb7654df(父提交 3914c49 是本 PR 上一版 head,另一父 7cf4cda 是当时的 upstream/main)。CI 按此 head:check pass(10m26s)、frontend pass(3m33s)。

上一轮我请求的阻塞项已经解决:PR 描述补上了「ADR 状态」一节。我按 specs cacbd19 核对,三条声明都成立——根决策 decisions/cloud/workflow/0-cloud-side-scheduler-owns-workflow-execution.md 仍是 approved,本 PR 实现的是它 D3 的创建与引用部分;20261015-executor-slots-replace-agent-model-ids.md 仍是 approved,本次未改槽位语义;20261016-asset-import-and-published-snapshots.md 已是 implemented,本次只补调用证据。本次不开改 implemented 的 specs PR 是对的。

本次增量没有要改的代码

  • 1ae5e42 的合并分辨率只动了四处:迁移改名 0044→0045、中英迁移 README 的 0045 小节、升级测试常量 workflowInvocationMigration。PR 自有代码与上一版逐字相同(git diff 3914c49 1ae5e42 -- <本 PR 新增与修改的代码、测试、前端> 为空),所以上一版对三入口共用创建函数、身份与 Project 冻结、迁移 SQL、投影函数与前端的逐条核对结论继续有效。
  • 迁移编号按 memory/rules/migration-numbering.md 核过:upstream/main(4cc9c2d)最新为 0044,本 PR 新增 0045,无同号;0035–0044 与 main 逐字相同(blob 一一比对);中英迁移 README 的 0044(main 的模型尝试登记)与 0045(本 PR)两节成对,0045 写明接在 0044 之后。
  • 上一轮建议 1 已落实:cloud#94(ed51486)在 main 上补回 SlotName 绑定,并新增 TestExecutorSlotMappingHTTPBindsSlotPath,走真实 HTTP 覆盖 named-slot PUT / DELETE;本分支 router.go 与 main 相同,不再携带这处修改。
  • 交付物:36 个改动文件全是代码、测试与正式文档,没有计划、进度或实现日志;git diff --check 干净;git grep 未发现把调用引用迁移残留写成 0044 的引用。

为什么现在不能 Approve

不是这次提交有缺陷,而是本 PR 自己的「未包含」清单里还有一项属于本 PR 的义务:投影的调用点(作者已在 circus#158 说明正在做、会作为后续提交推到本 PR)。在这个 head 上合入会得到这样的用户可见结果:

  • projectWorkflowRunToIssueRun 没有生产调用点(git grep 只命中文档和测试):main 的调度器会推进 WorkflowRun,但关联的 IssueRun 永远停在 queued;
  • enqueueRun 的 pending_run_exists(internal/core/issue_run_lifecycle.go:81)对同一 issue、同一 workflow 的再次 @ 返回 409,dispatchRun 又在 issue_run_lifecycle.go:122-126 直接跳过带引用的行,于是运行做完了也开不了下一次;
  • 前端对这类行显示的是不再推进的 queued。

按 circus#158 里定下的顺序,把接线提交推上来后再打 status:in-review:

  1. 调度器在认领 pending→running 成功之后、结算比较交换成功之后调用投影;
  2. 调度器侧测试断言结算前先写 started_at / finished_at,否则投影 409 会把整次结算回滚(PR 正文已列这条义务);
  3. 摘要由调用方截断到 20000 以内再传入,只让真正的契约冲突 fail-closed(上一轮建议 2)。

我在新 head 上确认这三点与 CI 后给 Approve,并在 circus#158 打 review:lgtm 时写明审过的 commit SHA。

一点提醒

main 在本 PR 合并 7cf4cda 之后又前进到 4cc9c2d(#61、#87),没有新增迁移,GitHub 仍报 MERGEABLE,不需要为此再合一次;最终合并前按当时的 main 再核一次迁移编号即可。

Refs ora-space/circus#158

Project only successful workflow status writes in the same transaction. Propagate settle CAS outcomes before session dispatch or cleanup, and truncate existing output/error summaries at UTF-8 boundaries within 20000 bytes.

Add PostgreSQL coverage for Confirm lifecycle, repeat invocation after completion, deterministic failure, concurrent passes, lost CAS, summary limits, and atomic rollback. Validate with task format, go run ./cmd/checkformat, task lint, targeted DB tests, and the requested core/integration suite.
creegon2 and others added 2 commits October 19, 2026 19:26
Keep Chinese and English workflow, collaboration, and core documentation aligned with same-transaction projection, affected-row guards, bounded summaries, and direct-run/awaiting-input no-ops.
main gained 0045_agent_configuration_catalog, so the invocation reference
migration moves to 0046; the migration READMEs list main's 0045 before it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@wanglongan587 wanglongan587 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve — 投影接线三项满足

审的 commit:f8331354ed46a13a25da5f8edac2303ab87497de(base upstream/main 0f4e60b;本版两个提交 713b191 接线、fb34d82 文档,再合并 main)。

上一轮要求的三件事

  1. 调用点:advanceWorkflowRun 在认领 pending→running 的比较交换更新到行后调用 projectWorkflowRunToIssueRun;settleWorkflowRunStates 改为返回 affected rows,只有更新到行才投影。会话成功、失败、取消都经 finishWorkflowRun → settle,启动超时同理。CAS 没更新到行时 finishWorkflowRun 跳过 Workspace 清理、startWorkflowAgentSession 不再入队会话工作项、commitWorkflowAgentTerminal 的 cont 为 false,由下一轮按已提交状态重算。直接运行的开发模拟器分支同样只在更新到行后投影,@ 与 manual 不经过它。生产里所有 UPDATE workflow_runs SET status 都在这三处,没有第四条绕过投影的路径。
  2. 时间戳先写后投影:认领的同一 UPDATE 写 started_at,终态结算的同一 UPDATE 写 finished_at,投影再校验(running 要求 startedAt 非空、终态要求 finishedAt 非空)。TestWorkflowConfirmSchedulerProjectsRunningOnce / ...CompletesAndAllowsNextInvocation 断言 IssueRun 的 started_at 等于 WorkflowRun 的、completed_at 等于 finished_at,run.started 只有一条,终态后同一 Issue 能再次 Confirm。
  3. 摘要截断在调用方:workflowIssueOutputSummary 从输出节点结果或运行错误生成,按 UTF-8 rune 边界截到 ≤20000 字节,两个调用点先截断再传入,投影里的 20000 守卫不再回滚结算。TestWorkflowSettleTruncatesIssueSummary 覆盖超长 error 与超长 output 两种来源(19997–20000 字节、合法 UTF-8)。

本地复跑(PR head,真实 PostgreSQL 18,独立 schema)

  • go build ./...、go run ./cmd/checkformat 通过。
  • go test ./internal/core -run 'TestWorkflow':通过,含 TestWorkflowAdvanceProjectsConcurrentPassesOnce、TestWorkflowSettleLostCASDoesNotProject、TestWorkflowSettleTruncatesIssueSummary、TestWorkflowAdvanceProjectionFailureRollsBackClaim。
  • go test ./integration -run 'TestWorkflowConfirmScheduler|TestWorkflowInvocation|TestManualWorkflowInvocation|TestImportedWorkflow|TestWorkflowConfirm':通过,含三个新调度器投影用例。
  • CI 按此 head:check pass(10m59s)、frontend pass(3m41s)。

其他

  • 迁移 0046 接在 main 最新的 0045 之后,无同号;中英迁移 README 的 0045 / 0046 两节成对。合并前按当时的 main 再核一次编号即可。
  • 相对 main 的 39 个改动文件全是代码、测试与正式文档,没有计划或进度记录;合并 main 没有改动 PR 自有代码(scheduler 相对 main 只有接线改动,session / lifecycle / core README / docs 亦然)。
  • 文档中英成对,已从"调用点未接入、IssueRun 保持 queued"改为"实际状态更新成功后在同一事务投影"。
  • preflight 未完成项(fixtures=false 真实 Node 联合验收)在描述里仍然如实列出;ADR 保持 approved 的声明成立。

非阻塞

workflowIssueOutputSummary 的 for !utf8.RuneStart(message[end]) 在"开头就是 20000 个以上续字节的非法 UTF-8"上会越界;现有来源(数据库 error 文本与 jsonText)不会出现,留作后续防御性写法即可。

Refs ora-space/circus#158

creegon2 and others added 2 commits October 19, 2026 20:17
main gained migrations 0046-0048, so the invocation reference migration
moves to 0049. enqueueRun keeps main's task-conversation admission checks
and enqueueAdmittedRun, and both now insert through
enqueueRunWithInvocation so the workflow invocation reference is written
with the IssueRun. The IssueRun type keeps both workflowInvocationRef and
main's triggerEvidenceRefId.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main replaced the core and migration READMEs with package and migration
maps, so the invocation linkage moves into that format: the workflow*.go
row names the shared WorkflowRun creation, the invocation reference and
the IssueRun status projection, and the migration map gains 0049 with
its settlement and constraint notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
creegon2 and others added 4 commits October 19, 2026 23:37
…on linkage

main gained 0049_thread_commands_workflow_run, so the invocation reference
migration moves to 0050 in the migration map. main's new session end
deadline settles through finishWorkflowRun, so it projects onto the linked
IssueRun like every other status write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in cloud#91 (Node-unreachable settlement of in-flight workflow sessions), cloud#100,
cloud#104 (workflow globals and sys.*) and cloud#105. The only textual conflict was the import
block of workflow_scheduler_db_test.go; advanceWorkflowRun now takes the deployment's
unreachability window, so the projection rollback test passes store.DeliveryUnreachableAfter
like AdvanceWorkflowRunsOnce does. The new Node-unreachable settlement goes through
finishWorkflowRun -> settleWorkflowRunStates, so it projects onto the linked IssueRun as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in cloud#83, cloud#101, cloud#102 and cloud#107. cloud#101 adds
0050_personal_agent_configurations.sql, so the invocation-reference migration moves on to
0051_workflow_invocation_reference.sql: the file is renamed unchanged, the integration test's
migration constant follows it, and both migration READMEs list 0050 and 0051 in order. main's new
`delete(input, "agentConfiguration")` lands in enqueueRunWithInvocation, the single writer that both
enqueueAdmittedRun and the workflow confirm path go through.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in cloud#108 (workflow session turn deadline) and cloud#109 (workflow run cancellation).
cloud#109 adds 0051_workflow_run_cancel.sql, so the invocation-reference migration moves on to
0052_workflow_invocation_reference.sql: the file is renamed unchanged, the integration test's
migration constant follows it, and both migration READMEs list 0050, 0051 and 0052 in order.
Every new settlement path (turn deadline, cancel-before-start, cancel of a running run) goes
through finishWorkflowRun -> settleWorkflowRunStates, so each projects onto the linked IssueRun;
a cancel before the claim projects queued -> cancelled without inventing a start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@creegon2

Copy link
Copy Markdown
Contributor Author

新 head 08102c2:在 140849f 之上合并 main 三次(只 merge,不 rebase、不强推),按 owner 的要求把迁移改号为 0052。

  • 迁移:cloud#101 占用 0050_personal_agent_configurations,cloud#109 占用 0051_workflow_run_cancel,所以调用引用迁移改名为 0052_workflow_invocation_reference.sql,文件内容不变。升级测试里的常量 workflowInvocationMigration 也跟着改了,升级基线用 migrationsBefore 计算,会自动包含 0050、0051。中英两份迁移 README 的表格和「不可变性与升级」小节都按 0050、0051、0052 排序。
  • 与 main 新增结算路径的衔接:
    • cloud#91 的 Node 不可达、cloud#108 的单轮超时,以及 cloud#109 的开始前取消和运行中取消,都是经 finishWorkflowRun → settleWorkflowRunStates 结算的,所以同样投影到关联的 IssueRun。
    • 开始前取消时,started_at 为空,投影为 queued → cancelled,不补写 run.started。
    • advanceWorkflowRun 新增了不可达窗口参数。投影回滚测试按生产的做法传入 store.DeliveryUnreachableAfter。
  • 验证:
    • go build、go vet、checkformat、task lint(0 issues)都通过;前端完整 npm run check 通过(1160 个用例)。
    • 带真实 PostgreSQL 跑 internal/core 的 Workflow/IssueRun 用例:全部通过。
    • 跑 integration 的 Migration/Workflow 用例:292 个通过。唯一失败的是 main 上 cloud#83 带来的 TestWorkflowAssetImportDoesNotFanOutToWorkspaces,在机器高负载时模拟 Node 报 node_not_ready,单独重跑通过。
  • 推送后 main 又合了 cloud#82:只改前端,与本分支试合并无冲突,也没有新迁移,所以这次没有再追。

@wanglongan587 @Zpc001 请按 08102c2 增量复审。相对 140849f 的实际改动只有:迁移改名、两份 README、测试常量,以及回滚测试的一行调用参数。

🤖 Generated with Claude Code

@Zpc001 Zpc001 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

增量复审 head 08102c2:迁移顺延 0052 无同号、README 中英成对;main 新增的 Node 不可达(cloud#91)、单轮/会话超时(cloud#108/#96)、取消(cloud#109)路径均经 finishWorkflowRun→settleWorkflowRunStates→projectWorkflowRunToIssueRun 投影,且 settle 仅在 CAS 更新到行后投影;CI check/frontend 全绿。批准。

@wanglongan587 wanglongan587 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审 cloud#71 @ 08102c2:Request changes(迁移 README 文件数、PR 描述的过期陈述)

审过的 commit:08102c2f7b18c694fa4f0f8f27d0dafa83d9cf1d(增量范围 f8331354..08102c2;本 PR 最后一次合并的 main 为 08102c2^2 = 27e7f48,当前 upstream/main = b0ae8c7)。CI 按此 head:check pass(13m19s)、frontend pass(3m44s)。

上一轮之后的增量

  • 迁移只改名:head 的 internal/core/migrations/0052_workflow_invocation_reference.sql 与 f8331354 的 0046_...sql blob 完全相同(7229efe3f62870477b6d4606025d9e25c635f7bc);链路 0046→0049→0050→0051→0052,每次只改文件名;测试常量 workflowInvocationMigration 已同步。
  • 投影接线与 f8331354 审过的实现逐字一致;workflow_run.go 少了 startedAt/finishedAt 的 run.S(...)→run[...] 补丁,是因为 main 的 9e1e4fb(fix(core): keep unset workflow run timestamps null)已实现同一修复,本 PR 不再携带。
  • PR 侧代码改动只有一处测试适配:internal/core/workflow_scheduler_db_test.go 的 advanceWorkflowRun(tx, s.workflowRun, store.DeliveryUnreachableAfter);issue_run_lifecycle.go 的合并冲突解决把本 PR 的 enqueueRunWithInvocation 接到 main 新增的 enqueueRun→enqueueAdmittedRun 链上,main 的任务会话准入保留;前端 types.ts 同时保留 workflowInvocationRef 与 triggerEvidenceRefId。
  • 其余有 patch 文本差异的文件,差别都来自 main 的上下文/行号(逐文件比对增删行一致),文档与前端代码本身没有新改动。

核对通过

  1. 迁移编号:head 最高 0052,main(b0ae8c7)仍以 0051_workflow_run_cancel.sql 为最高,无同号;27e7f48..b0ae8c7 的 8 个 main commit 没有新增迁移,也没有触及本 PR 的 39 个文件,PR 仍 MERGEABLE。另三个开放 PR(#111/#106/#74)也还挂着 0051,先合者会占 0052 及之后,合并前仍按 memory/rules/migration-numbering.md 复核。
  2. 迁移 README 顺序:zh/en 的 0052 表行与 bullet 都紧随 0051,两语言一一对应;bullet 的结算、复合外键、唯一引用、CHECK、无条件 VALIDATE 与 SQL 逐条对应。
  3. 模块 README 与文档:internal/core/README(.en).md 的 workflow*.go 一行已按 main 重建后的代码地图格式改写,链接目标存在;12 个改动文档 zh/en 成对、锚点可解析,旧的「工作流 IssueRun 保持 queued」等说法已更新。
  4. 状态投影:认领 CAS 成功后(workflow_scheduler.go:494-498)投影;settleWorkflowRunStates 返回 affected rows,只有更新到行才投影(:623-648);main 新增的 Node 不可达、单轮超时、会话结束超时、取消(含开始前取消)都经 finishWorkflowRun → settle 投影;cancelWorkflowRunByUser 只写 cancel_requested_at,不改 status。开始前取消投影为 queued→cancelled,因 startedAt 为空不会补写 run.started(workflow_issue_projection.go:94);摘要由调用方按 UTF-8 截到 ≤20000 字节,正常结算不会因活动大小回滚。
  5. 三入口共用 insertWorkflowRun,Project 缺失统一 409 workflow_run_project_required;Confirm/manual 走 enqueueWorkflowInvocation 冻结调用者与 Issue Project,直接启动取请求体;生产代码里 workflow_runs.status 的写点只有认领、结算、开发模拟器三处,都经投影。
  6. 交付物:39 个文件 = 1 迁移 + 9 Go + 10 Go 测试 + 7 前端 + 12 文档,无计划/进度/实现日志。
  7. ADR 状态:按 specs 6aad4f9 核对,根决策 0-cloud-side-scheduler-owns-workflow-execution.md 仍 approved、20261015-executor-slots-replace-agent-model-ids.md 仍 approved 且本次未改槽位语义、20261016-asset-import-and-published-snapshots.md 已是 implemented;本 PR 只实现 D3 的创建/引用/投影,fixtures=false 的真实 Node 联合验收未交付,保持 approved、暂不开 specs implemented PR 的判断成立。

需要修改(阻塞)

  1. 迁移 README 的文件数没更新。 head 的 internal/core/migrations/ 有 54 个 .sql(main 53 个 + 本 PR 的 0052),但 README.md:9 仍写「当前共 53 个 SQL 文件。」、README.en.md:9 仍写「There are currently 53 SQL files.」。请两语言一起改成 54。这正是 owner 在 circus#158 明确请 reviewer 核对的「迁移 README 的顺序与文件数」中的文件数一项。
  2. PR 描述有过期陈述(描述是 owner 合并判断的依据,请一并更新):
    • 「改动」第 3 条:迁移「编号接在 main 已有的 0049 会话命令归属迁移之后」→ 实际前驱是 0051_workflow_run_cancel.sql(0049、0050 在中间)。
    • 开头「preflight 仍未包含 …(等 circus#70)」与「ADR 状态」节「由 circus#158 的后续验收与 circus#70 承接」:circus#70 已于 2026-10-20T08:09Z 关闭,本运行已转 run,请改为由本运行的剩余验收 / #77 协调承接。
    • 「未包含 / 待对齐」第 1 条写的是已交付的投影接线,请移到「改动」或改为已包含,避免和真正的未包含项混在一起。
    • 「改动」的「修复:pending WorkflowRun 详情…」一条:main 的 9e1e4fb 已实现同一修复,本 PR 的 9e0fcb2 与它重复(相对 main 已不产生 diff,对应集成测试仍在),请在描述里注明已被 main 吸收,或删掉这条,避免让人以为本 PR 还带这个实现。若愿意顺手清理,也可以在合入前把这个空提交从分支里去掉;不清理也不阻塞。

建议(非阻塞)

  • 0052 的 bullet 可补一句它同时给 workflow_runs 加了 workflow_runs_id_tenant_uniq(复合外键的前置唯一约束)。
  • 「三个入口共用创建函数」可点名 insertWorkflowRun(顶层 createWorkflowRun 只服务直接启动);docs/workflows/workflows(.en).md 同一句也略宽。
  • workflowIssueOutputSummary 里 for !utf8.RuneStart(message[end]) 在开头就是 20000+ 续字节的非法 UTF-8 上会越界;现有来源不会出现,可留后续防御性写法。

改动后请在 circus#158 重新打 status:in-review,我在新 head 上复审。另:GitHub 的 reviewDecision 目前仍是 CHANGES_REQUESTED(EricWvi 2026-10-18 对 ecd2589 的 review),新 head 也需要一次覆盖它的审查;按 #158 13:05Z 的交接,后续开发由 @Zpc001 的接续运行承担,本结论只对 08102c2 成立。

@wanglongan587

Copy link
Copy Markdown
Collaborator

补充:独立核验的非阻塞观察(不改变本次 Request changes 的两条)

  1. 更正我在 review 里的一句话:workflow_runs.status 的写点严格说有四类——创建 INSERT ... 'pending'(workflow_run.go:159,同一事务随后插入 queued IssueRun,初值一致,无需投影;直接运行没有 IssueRun)、开发模拟器 UPDATE(:135,affected>0 才投影)、认领 CAS(workflow_scheduler.go:494-498)、结算(:630-648)。没有找到「状态迁移未经投影」的反例。

  2. 0052:11 的 workflow_runs_id_tenant_uniq 与 main 0044_model_attempt_workflow_run.sql:18 的 workflow_run_tenant_identity 重复(两者都是 UNIQUE(id, tenant_id)):这是合并 main 后留下的冗余唯一索引,复合外键引用 0044 已有的约束即可。可在合入前删掉 0052 的这一行,属于清理项;不删也不影响正确性。

  3. 取消、Node 不可达、单轮超时、会话结束超时这几条 DB 测试(workflow_run_cancel_db_test.go、workflow_run_session_*_db_test.go)都是没有 IssueRun 的 direct run,缺少一条「linked IssueRun 端到端」用例。建议接续运行补一条:Confirm 建出 linked IssueRun 后、下一轮调度前取消,断言 IssueRun 变为 cancelled、started_at 为 NULL、没有 run.started;Node 不可达同理断言 failed。投影函数本身的映射与守卫已有单测覆盖。

  4. 如果 linked IssueRun 因异常已是终态,投影的 409 会让 WorkflowRun 每轮回滚、永不终结(fail-closed 的设计;正常 API 不可达——cancelAgentRunByUser 只接受 executor_type='agent')。留作后续观察即可。

以上四条不影响代码层结论:投影接入、CAS 语义、main 新增结算路径与共用创建函数都在 head 上成立;本次 Request changes 的两条(迁移 README 的文件数、PR 描述的过期陈述)是仅有的阻塞项。

The migration directory now holds 54 SQL files (main's 53 plus 0052), so both READMEs say so.
The 0052 entry also names `workflow_runs_id_tenant_uniq`, the unique (id, tenant_id) constraint
that the composite invocation foreign key requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@creegon2

Copy link
Copy Markdown
Contributor Author

@wanglongan587 两项阻塞都已处理,新 head 6699540:

  1. 迁移 README 文件数:internal/core/migrations/README.md:9 / README.en.md:9 改为 54 个 SQL 文件。顺带采纳一条非阻塞建议:0052 的说明里补上复合外键所需的 workflow_runs_id_tenant_uniq。推送前核对过,当前 main 的最高迁移仍是 0051_workflow_run_cancel.sql。
  2. PR 描述:已更新。迁移前驱改为 0051;去掉了「等 / 由 circus#70 承接」,改为 feat(workflows): start a run from the selected version's own form #70 已关闭、fixtures=false 验收补做时与 feat: import workflow plugin assets as tenant workflows #77 协调;已交付的投影接线移出「未包含」;时间戳一条注明 9e0fcb2 已被 main 的 9e1e4fb 吸收,相对 main 只多一个集成测试。

空提交 9e0fcb2 没有从分支里去掉。去掉它要改写历史,本 PR 一直只 merge、不 rebase。

这是交接前我的最后一个提交,之后由 @Zpc001 的接续运行从 6699540 继续。

🤖 Generated with Claude Code

@wanglongan587 wanglongan587 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审 cloud#71 @ 6699540:两项文档阻塞已解决;但 owner 的放宽结论还没落地,暂不给 lgtm

审的 commit:6699540325723d410aa8e98489f41bed18acf4d2(上一版 08102c2f7b18c694fa4f0f8f27d0dafa83d9cf1d,增量范围 08102c2..6699540)。按当前 upstream/main = ff72afa0d8dbebe77ec10231936221e17945c048 复核:最高迁移仍是 0051_workflow_run_cancel.sql,本 PR 的 0052 无同号;b0ae8c7..ff72afa 未触及本 PR 的迁移,GitHub 仍报 MERGEABLE / CLEAN。CI 按此 head:check、frontend 均 SUCCESS。

上一轮两条阻塞都已处理(已在 head 上核过)

  1. 迁移 README 文件数:internal/core/migrations/README.md:9 / README.en.md:9 已改为「54 个 SQL 文件」;head 的目录里正好 54 个 .sql(main 的 53 个 + 0052_workflow_invocation_reference.sql),中英一致;顺带补在 0052 bullet 里的 workflow_runs_id_tenant_uniq 与 SQL 第 11 行对得上。
  2. PR 描述:迁移前驱改为 0051_workflow_run_cancel.sql;「等 / 由 circus#70 承接」改为 #70 已关闭、fixtures=false 验收补做时与 #77 协调;已交付的投影接线移出「未包含 / 待对齐」;9e0fcb2 注明被 main 的 9e1e4fb 吸收、相对 main 只多一个集成测试。Refs 已指向接续运行 circus#374。

增量只有这两份 README(2 个文件、4 行),代码与上一轮审过的 08102c2 逐字相同,所以上一轮对迁移、投影接线、三入口共用创建函数、main 新增结算路径的核对结论继续有效。

现在不能 Approve:owner 13:35Z 的放宽结论未落地

@EricWvi 在 circus#158 13:35Z 的结论要求「Confirm 只按用户所选版本自身的字段校验启动输入,不再拿活稿比较,删除 workflow_version_fields_changed 这条门禁」。本 head 仍在拦:全仓唯一的生产调用点在 internal/core/issue_interactions.go:149-151,workflowFieldDivergence(liveFormDescriptor(...), versionDescriptor) 非空即 409 workflow_version_fields_changed。

需要按 owner 结论一起调整的地方:

  • 后端:issue_interactions.go:149-151;删除后 workflow_collaboration.go:232 的 liveFormDescriptor、:303 的 workflowFieldDivergence 没有其他生产调用点,可一并删除,workflow_collaboration_test.go:914 的用例随之处理;workflow_collaboration.go:247、form_descriptor.go:325 的注释也指向这条门禁。
  • 集成测试:integration/workflow_interaction_test.go:184 的 refusedFields 与 :997 / :1018 / :1039 三处断言;按 owner 要求补「选字段与活稿不同的旧版本 Confirm,按旧 schema 通过」与「缺旧版本必填字段时按旧 schema 拒绝」。
  • 前端:frontend/src/features/issues/components/workflow-interaction-composer.tsx:51 的 409 提示路径、workflow-interaction.test.tsx:245-248 的用例,以及 frontend/src/features/issues/components/README.md:36 / README.en.md:36 的「门禁保持不变」说明。
  • 文档:docs/collaboration/interactions.md:72-73 与 interactions.en.md:96-100 把这条写成了行为,要改成按所选版本校验。
  • PR 描述:「未包含 / 待对齐」第 2 条仍写「是否放宽由 owner 决定」,已过期,请改为按 owner 结论已放宽。

按 owner 的交接,这些由接续运行 circus#374 实现;实现后在新 head 上复审。本结论只针对尚未落地的 owner 结论,不是对 6699540 增量的否定——增量本身没有问题,只是它不能作为合并版本。

两条非阻塞(与 @creegon2 13:41Z 转给接续运行的一致)

  • 0052_workflow_invocation_reference.sql:11 的 workflow_runs_id_tenant_uniq 与 main 0044_model_attempt_workflow_run.sql:18 的 workflow_run_tenant_identity 重复(都是 UNIQUE(id, tenant_id))。删掉后,6699540 在 README 补的那句说明也要一起去掉。
  • 建议补一条 linked IssueRun 的端到端投影用例:Confirm 建出 linked IssueRun 后、下一轮调度前取消 → IssueRun cancelled、started_at 为 NULL、没有 run.started;Node 不可达 → failed。

复审轨迹

f833135(Approve)、08102c2(Request changes:迁移 README 文件数、PR 描述)、6699540(本结论:增量通过,等 owner 结论落地后的新 head)。

Refs ora-space/circus#374(原运行 circus#158 已由作者归档)

@EricWvi
EricWvi merged commit 6699540 into ora-space:main Oct 21, 2026
2 checks passed
@creegon2

Copy link
Copy Markdown
Contributor Author

说明:本 PR 由 cloud#114 接续(@Zpc001 在本分支最后一个提交 6699540 之后续作)。owner 合并 cloud#114(merge commit 0db4230)后,本 PR 的提交已全部进入 main,GitHub 因此自动标为 merged。审查与测试历史以 cloud#114 为准;剩余收尾在 ora-space/circus#374。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants