Files
Agent-System/.doc/audit/2026-09-13-controlled-doc-client-sync-ui03.md
T
weichenandCursor 7466a15319 fix(docgov): harden UI-03 sync poll, reconnectRunId, and E2E status attrs
Use deliverablesRef + limited poll for post-task server docs; pass
reconnectRunId across auto-retry; GetRun reads idempotency_key;
StartClaimedRun fails closed on UpdateRun; MessageBubble/E2E scope
this-turn assistant; correct audit retry/schema19 wording.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 13:54:49 +00:00

7.6 KiB
Raw Blame History

title, status, created, updated, severity
title status created updated severity
小队完成未同步受控文档状态 + UI-03 选择器非 Tab + Retry 暴露 DB 约束(UI-03) done 2026-09-13 2026-09-13 critical

小队完成未同步受控文档状态 + UI-03 选择器非 Tab + Retry 暴露 DB 约束(UI-03)

1. 现场实战概况与判定

在真实 UI 验收(UI_PHASE=main,dev 环境 schema 29,真实 pi-agent + MiniMax-M2.7-highspeed):

  • UI-00 PASS(4.9s):管理员真实创建 A/B/C,准确按 DOM 作用域关闭模态,三用户完成首次登录改密。
  • UI-01 PASS(774ms):A 页面立项并分配 B 为 reviewer,项目工作台就绪,零 404,模板预览正确。
  • UI-02 PASS(191ms):真实发任务,POST /api/v1/tasks/runs 返回 202 Accepted,小队编排启动;真实模型耗时约 157 秒完成编排,后端成功生成受控文档 doc_proj6089_user_requirements(质量评分 100/100)。
  • UI-03 FAIL:await expect(page.locator('[data-testid="doc-rev-version"]')).toBeVisible({ timeout: 60_000 }); 超时失败。
    • 页面真实状态:页面已明确显示小队任务完成,正文标出 📄 受控文档:doc_proj6089_user_requirements。
    • 右栏面板状态:右栏仍保持为 doc-p intake 初始模板(用户初始需求收集表 模板,模板预览:首轮编制后启用版本账本),版本工具条 [data-testid="doc-rev-version"] 根本未挂载。
  • UI-04 ~ UI-16:NOT RUN(因 UI-03 失败立即停机保全现场,未执行后续用例)。

2. 根因与现场异常分析

A. 产品缺陷:前端在小队完成后未同步工作流文档列表

  1. 服务端已落库受控文档: 后端 document_records 表已落库 doc_id="doc_proj6089_user_requirements",is_template_only=false,current_state="drafting"。且前端每 5 秒发起的轮询 GET /api/v1/projects/6089/documents?category=workflow 已正确返回该受控文档记录。
  2. syncLeases 轮询丢弃新文档: 在 web/src/App.tsx 第 1664–1697 行,每 5 秒执行的 syncLeases 仅提取 doc.lease_holder_type 并更新既有 doc.docId 的租约。由于本地 React 状态 deliverables 初始仅有 doc-p6089-* 模板,leases.has(doc.docId) 匹配不上 doc_proj*,新文档从未被合并进 deliverables。
  3. syncWorkflowDocs 依赖项未触发: 在 App.tsx 第 1618–1661 行,syncWorkflowDocs(调用 mergeWorkflowDocumentsFromServer)仅在 currentProjectId 或 session 变动时执行;同一会话内小队完成不会触发该 effect。
  4. 流式结束 case 'done' 遗漏文档同步: 在 App.tsx 第 2571–2593 行,任务流完成时仅调用了 await chatWindow.refresh().catch(() => undefined)(仅刷新聊天消息),未同步工作流文档列表。
  5. 连锁阻断工具条挂载: 前端 deliverables 中的 user_requirements 一直是 isTemplateOnly: true。根据 revisionToolbarMountMode 契约,isTemplateOnly === true 时返回 'template_hint',阻断 DocumentRevisionToolbar([data-testid="doc-rev-version"])挂载。

B. 测试脚本缺陷:定位器假设水平 Tab 且命中页头 Banner 徽章

  1. 在 web/e2e/document-revision-runtime.spec.ts 第 538 行: const tab = page.locator('[role="tab"], button, div').filter({ hasText: /需求|摸底|URS|用户/ }).first();
  2. 穿透到页头徽章: [role="tab"], button, div 包含全局所有 div。页面顶部 Header Banner 含有 当前项目: ... V0.1 需求摸底(div 元素)。.first() 命中了该非交互的 Banner 徽章 div,根本没有点击文档切换器。
  3. 真实 UI 为下拉/抽屉交互: SlimTabBar.tsx 为面包屑下拉(button.dropdown-trigger -> .dropdown-menu)或大盘抽屉(DeckDrawer),页面不存在水平文档 Tab。

C. 前端断线自动重试与 DB 约束异常暴露

  1. 前端断线自动重试导致同 turn 二次触发(非 Playwright retries): web/playwright.config.ts 中 retries: 0。现场 13:02 完成首轮后,13:05 再次出现同 prompt 处理中,根因是前端 runTaskAttempt 在连接异常路径上自动重试,且重试开头 setRunId(null) / 未显式传递 reconnectRunId,导致重复 POST /tasks/runs 而非订阅既有 run:
    • 13:02:TRIGGER_TASK 请基于本项目编制首轮... → 《用户需求表》已生成。当前状态:已完成
    • 13:05:同 turn 自动重试路径再次进入处理中(工程修复:reconnectRunId 跨 attempt + Claim 幂等)
  2. 暴露 task_runs 状态约束缺陷: 在二次运行与高风险候选流转路径中,后台服务尝试将 task_runs.status 更新为 pending_author_confirmation 时,暴露了数据库底层约束拦截: goose 00019 原始 chk_task_run_status 仅允许:
    CHECK (status IN ('pending', 'running', 'completed', 'failed', 'cancelled', 'finalizing_failed', 'quality_failed', 'interrupted'))
    
    该枚举未包含 pending_author_confirmation,导致写入状态时触发 PostgreSQL check constraint 违规(由 goose 00030 补齐)。

3. 严重度与阻断

  • 严重度:critical
  • 阻断项:
    1. 产品前端:App.tsx 在小队完成(case 'done')以及定期轮询中必须调用 mergeWorkflowDocumentsFromServer 动态更新受控文档。
    2. 数据库约束:task_runs 表的 chk_task_run_status 约束必须补齐 pending_author_confirmation。
    3. 测试脚本:修正 UI-03 文档切换定位器,禁止使用全局 div 通配,走真实 SlimTabBar 下拉或 DeckDrawer 动作;关闭非幂等的单用例重试。

4. 证据保全清单

  • 归档路径:.doc/test-reports/assets/2026-09-13-document-revision-runtime-ui/ui03-controlled-doc-sync-failure/
    • playwright-output/(含完整 trace.zip、test-failed-1.png、error-context.md)
    • playwright-report/(HTML 完整执行报告)
    • screenshots/(UI-00~UI-02 真实过程截图)
    • traces/、videos/
    • ui-acceptance-results.json(UI-00~UI-02 逐项 PASS 记录)
    • phase-secrets.local.json、phase-state.json
  • 历史归档目录全部原位保全:
    • post-auth-ui00-backdrop-click-failure/
    • post-auth-ui00-close-selector-failure/
    • post-auth-ui00-wrong-close-locator-failure/
    • pre-auth-fix-ui00-failure/
    • ui01-parent-directory-sync-failure/
    • ui01-strict-mode-title-selector-failure/
    • ui02-task-scope-409-failure/
    • ui02-workflow-access-failure/

5. 工程修复落盘(fix/document-revision-runtime-ui03,合入前)

根因 修复 门禁
A 任务完成后未 merge 受控文档 pollUntilPostTaskServerDoc + deliverablesRef 显式快照(禁止 setState updater 取 mergedSnap)+ 有限重试至 doc_proj* focused + npm test / build
A′ intake vs URS 误匹配 templateMatch:intake 标记优先于裸「用户需求」 单测 PASS
B UI-03 选择器命中 Banner SlimTabBar/DeckDrawer;data-chat-role/status + 本轮 assistant sibling 等待;UI-11 禁止全局 div 待真实 UI 重跑
C pending_author_confirmation 约束 goose 00030 扩展 chk_task_run_status(相对 00019 枚举) 真实现卷迁移合入前独立验证
C′ 断线自动重试重复 POST reconnectRunId 显式跨 retry;ClaimRun;GetRun 读回 idempotency_key;StartClaimedRun UpdateRun 失败不启 worker go test -short ./internal/taskrun

未做:占槽 rebuild、真实 UI UI-03 重验、schema30 对现有重复 (session_id,client_turn_id) 卷的迁移实测。