Files
ai-contract-platform/project-docs/V2-phase3-reject-legacy-next-action-input-2026-07-02.md
T
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

4.1 KiB
Raw Blame History

AI 合同平台 V2 Phase 3 拒绝旧 next_action 输入

背景

30 分钟 smoke 观察窗口显示旧 next_action / next_actions alias 没有新增调用:

  • baseline_total=1
  • final_total=1
  • observed_delta=0
  • relevant_new_count=0

因此进入 cleanup 的 Slice A:先收紧接口输入,不动数据库结构。

本次调整

PATCH 输入收紧

合同更新接口:

PATCH /api/v1/contracts/{contract_id}

现在行为:

  • action_hint:继续支持,作为人工跟进提示唯一写入字段。
  • next_action:拒绝,返回 422。
  • next_actions:拒绝,返回 422。

错误响应示例:

{
  "detail": {
    "code": "deprecated_contract_update_action_alias",
    "message": "Deprecated contract action fields are no longer accepted. Use action_hint for manual follow-up text.",
    "fields": ["next_action", "next_actions"],
    "replacement_field": "action_hint"
  }
}

观测保留

旧字段被拒绝时仍会写入观测窗口:

  • alias_group=contract_update_action
  • fields=["next_action","next_actions"]
  • target_field=action_hint
  • compatibility_status=rejected

观测记录仍不保存旧字段文本值。

不动数据库结构

本片没有执行 Alembic migration。

继续保留:

  • contracts.next_action
  • contracts.next_actions

原因:

  • 响应字段 next_action / next_actions 仍由派生层对外保留。
  • 部分 read/search/schema-check 路径仍把旧列作为 retained compatibility columns。
  • 真正删列应放到后续 Slice C。

OpenAPI

ContractUpdateRequest.next_action / next_actions 描述已更新为:

  • deprecated。
  • no longer accepted by write paths。
  • use action_hint instead。

验证

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_update_legacy_action_alias_usage_is_observable `
  -q

预期:

  • action_hint PATCH 返回 200。
  • next_action / next_actions PATCH 返回 422。
  • 旧字段文本值不会进入错误响应或 metrics。
  • metrics 中 compatibility_status=rejected。

本地结果:

  • 聚焦回归:2 passed。
  • py_compile:通过。
  • test_health.py + test_monitoring.py:17 passed。
  • OpenAPI YAML 解析:3.1.0 / 51 paths。

远端 smoke 验证

环境:

  • 宿主机:192.168.3.90
  • Contract API:http://127.0.0.1:18100
  • compose project:yuqei-contract-v2-smoke

部署结果:

  • contract-api 已重建并启动。
  • contract-api 容器状态:healthy。
  • /api/v1/runtime/schema-check 返回 status=ok。
  • display_cache_consistency.mismatch_count=0。
  • display_cache_cleanup_prestate.status=ok。
  • 旧列仍保留:retained_compat_fields=next_action,next_actions。

接口抽样:

  • smoke 合同:contract-e51a2edfa899
  • action_hint PATCH:返回 200。
  • 旧 next_action / next_actions PATCH:返回 422。
  • 错误 code:deprecated_contract_update_action_alias。
  • 合同详情仍保持:action_hint=supported-action-hint-smoke。
  • 响应派生字段仍返回:next_action=supported-action-hint-smoke、next_actions=["编辑合同","提交审批"]。
  • metrics 记录:
    • alias_group=contract_update_action
    • compatibility_status=rejected
    • fields=["next_action","next_actions"]
    • trace_id=trace-reject-legacy-alias-smoke
    • tenant_id=tenant-reject-legacy-alias-smoke
  • 旧字段文本值未进入错误响应或 metrics。
  • 最近 10 分钟 Deprecated contract update action alias used 日志计数为 1。
  • 最近 10 分钟 Contract display cache mismatch 日志计数为 0。
  • 3 秒观察窗口通过:baseline_total=1、final_total=1、observed_delta=0、relevant_new_count=0。

下一步

后续建议:

  • 再跑一个 30 分钟观察窗口,确认没有真实旧客户端继续调用。
  • 如果窗口仍为 0,进入 Slice B:停止旧列读取。
  • 继续暂缓 Slice C 数据库删列,直到 schema-check 和读路径都不再依赖旧列。