Files
ai-contract-platform/project-docs/V2-phase3-action-hint-ui-api-semantics-2026-07-01.md
yuqei a3bb78e06b
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 16m35s
release: freeze V2 release candidate
2026-07-12 15:32:24 +08:00

5.6 KiB
Raw Permalink Blame History

AI 合同平台 V2 Phase 3 action_hint 前端与接口语义收敛

背景

上一片已停写旧 contracts.next_action / contracts.next_actions 生产写路径,并确认 API 响应继续由完整 ContractActionContext 派生。

本片继续收敛产品语义:

  • action_hint 是人工填写的跟进提示事实字段。
  • next_action / next_actions 是系统派生的展示字段和兼容响应字段。
  • 旧 next_action 写入入口在观察窗口通过后已进入 rejected 阶段,不再规范化到 action_hint。

本次调整

前端编辑入口

合同详情编辑面板中的“人工跟进提示”输入框现在只使用:

  • contract.action_hint

不再 fallback 到:

  • contract.next_action

原因:

  • next_action 现在是系统派生动作,例如“等待审批人审批”“处理审查风险并应用推荐条款”。
  • 如果编辑表单默认带入派生动作,用户保存时会把系统动作误存成人工提示。

同时,编辑面板 Props 已移除 next_action 依赖,后续 TypeScript 会阻止该组件重新把派生动作当作人工提示默认值。

前端占位文案也同步收敛为“人工补充的跟进提示,不会覆盖系统下一步操作”,避免把 action_hint 理解成可手工改写系统派生动作。

API / OpenAPI 语义

后端 DTO 和 OpenAPI 已补充字段说明:

  • ContractUpdateRequest.action_hint
    • 支持写入。
    • 表示人工跟进提示。
  • ContractUpdateRequest.next_action
    • deprecated。
    • 不再接受写入。
    • 客户端应改用 action_hint。
  • ContractUpdateRequest.next_actions
    • deprecated。
    • 不再接受写入。
    • 响应按钮列表由 ContractActionContext 派生。
  • ContractListItem.next_action
    • 响应字段保留。
    • 来源是派生展示动作,不是持久化人工输入。
  • ContractDetail.next_actions
    • 响应字段保留。
    • 来源是派生动作按钮。

没有做

  • 未删除旧数据库列。
  • 未移除响应字段 next_action / next_actions。
  • 未移除旧 next_action / next_actions 请求字段定义,但写路径已拒绝旧字段。
  • 未进入 cleanup migration。

验证

python -m py_compile yuqei-contract-platform\services\contract-api\src\yuqei_contract_api\domain.py

python -m pytest `
  yuqei-contract-platform\services\contract-api\tests\test_contract_workflow.py::test_contract_update_accepts_action_hint_and_rejects_legacy_next_action `
  yuqei-contract-platform\services\contract-api\tests\test_contract_workflow.py::test_contract_action_hint_derives_next_action_without_overriding_buttons `
  -q

npm --workspace @yuqei/contract-web run typecheck

结果:

  • py_compile:通过。
  • 后端 action_hint 回归:2 passed。
  • contract-web typecheck:通过。

远端 smoke 验证

环境:

  • 宿主机:192.168.3.90
  • compose project:yuqei-contract-v2-smoke
  • Contract API:http://127.0.0.1:18100
  • Contract Web:http://127.0.0.1:13000
  • PostgreSQL database:yuqei_contract

验证结果:

  • contract-api / contract-web 容器均为 healthy。
  • /api/v1/runtime/schema-check 返回 status=ok。
  • display_cache_consistency:
    • checked_rows=12020
    • mismatch_count=0
    • checked_fields=status_label,status_tone
    • retained_compat_fields=next_action,next_actions
  • display_cache_cleanup_prestate:
    • status=ok
    • legacy_columns_retained=true
    • display_cache_consistent=true
    • next_step=stop_legacy_display_cache_writes_before_drop_columns
  • 旧 next_action PATCH 抽样:
    • smoke 合同:contract-19740a3a80f2
    • 请求字段:next_action=legacy-next-action-alias-to-action-hint
    • 响应字段:action_hint=legacy-next-action-alias-to-action-hint
    • 响应字段:next_action=legacy-next-action-alias-to-action-hint
    • 数据库字段:action_hint=legacy-next-action-alias-to-action-hint
    • 数据库旧缓存字段:next_action=''
    • 数据库旧缓存字段:next_actions=[]
  • 合同详情页面:/contracts/contract-19740a3a80f2 返回 HTTP 200。
  • 最近 10 分钟 Contract display cache mismatch 日志计数为 0。
  • 前端文案收敛后已重新构建并部署 contract-web:
    • Next build:通过。
    • 生产构建内置 TypeScript:通过。
    • contract-web 容器:healthy。
    • 合同详情页面:/contracts/contract-19740a3a80f2 返回 HTTP 200。

cleanup migration 评审结论

本轮不进入删列或移除响应字段的 cleanup migration。

理由:

  • next_action / next_actions 仍是对外响应兼容字段,前端和旧调用方可能仍读取。
  • 旧 next_action 写入口已经明确降级为 action_hint alias,保留它不会再污染旧缓存列。
  • schema-check 已把检查口径收敛到 status_label/status_tone,并明确标注旧动作列只作为兼容保留。
  • 后续真正删列前,需要先观测是否仍有外部客户端提交旧字段,并决定是否增加 alias 使用审计。
  • 旧 alias 使用观测已进入独立收口文档:V2-phase3-deprecated-next-action-alias-observability-2026-07-01.md。

当前结论

前端和接口语义已收敛为:

  • 人工输入只走 action_hint。
  • 系统展示只读 next_action / next_actions 派生响应。
  • 旧写入字段已经拒绝,不再作为兼容入口。

下一步仍建议先保持兼容观测,不直接删列。若继续推进 cleanup,应先检查:

  • 是否还有外部客户端提交 next_action / next_actions。
  • 是否需要在日志或审计中记录旧 alias 的使用次数。
  • 是否需要在后续版本中把 OpenAPI deprecated 字段推进到移除计划。