Files
ai-contract-platform/project-docs/V2-phase3-display-cache-cleanup-prestate-2026-06-30.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

7.3 KiB
Raw Blame History

AI 合同平台 V2 Phase 3 UI 缓存字段 cleanup prestate 固化

对齐范围

本记录对齐《AI 合同平台 V2 下一阶段开发优化方案》的 Phase 3 schema 硬化方向,承接 Slice 3.4 的 UI 展示字段派生和旧 UI 缓存治理。

当前结论不是进入删列 migration,而是先把“旧 UI 缓存字段仍保留但已一致”固化为 cleanup migration 前状态。

当前 smoke 基线

  • 权威 V2 smoke PostgreSQL:192.168.3.90 上的 yuqei_contract
  • Contract API:http://192.168.3.90:18100
  • Alembic actual/head:20260630_0021
  • display_cache_consistency.status:ok
  • display_cache_consistency.checked_rows:12010
  • display_cache_consistency.mismatch_count:0
  • 回滚 snapshot:/opt/apps/ai-contract-platform-v2-smoke/backups/display-cache/display-cache-backfill-candidate-2026-06-30T17-37-24.json
  • 本地备份:project-docs/qa-artifacts/display-cache-governance/2026-06-30T17-37-24/display-cache-backfill-candidate-2026-06-30T17-37-24.json

这表示 smoke 数据已经从“旧 UI 缓存存在 8000 条不一致”推进到“旧列仍保留、旧值与派生值一致”的可控状态。

本次工程化固化

新增 runtime schema-check 检查项:

  • display_cache_cleanup_prestate

检查目标:

  1. contracts.status_label
  2. contracts.status_tone
  3. contracts.next_action
  4. contracts.next_actions

检查语义:

  • 四个旧列仍存在,且 display cache mismatch 为 0:ok
  • 四个旧列仍存在,但 mismatch 不为 0:warning
  • contracts 表缺失:warning,由 core_tables 继续报 error
  • 任一旧列在正式 cleanup migration 前缺失:error

新增此检查的原因是:cleanup migration 前,旧列仍是兼容/回滚面;如果有人提前删列,应明确报错,而不是让 display cache consistency 变成模糊的 SQL/连接异常。

Cleanup migration 进入条件

只有同时满足以下条件,才建议设计删列 migration:

  • API 响应中的 status_label/status_tone/next_action/next_actions 已完全由 mapper/serializer 派生。
  • 后端业务写路径不再直接依赖四个旧列。
  • 前端不再提交旧 UI 缓存字段作为业务输入。
  • 搜索索引重建可以从事实字段和派生规则生成展示文案。
  • smoke 环境连续多轮 display_cache_consistency 与 display_cache_cleanup_prestate 均为 ok。
  • 保留并验证可回滚 snapshot。

建议拆分顺序

  1. Slice 3.5.1:确认 next_action 人工编辑入口是否保留。
  2. Slice 3.5.2:先停止手写 status_label/status_tone,但保留列和 API 字段。
  3. Slice 3.5.3:拆分 next_action/next_actions 语义,必要时迁移到 action_hint 或 action override 结构。
  4. Slice 3.5.4:再进入 cleanup migration 设计评审。

本次验证

python -m pytest yuqei-contract-platform\services\contract-api\tests\test_health.py yuqei-contract-platform\services\contract-api\tests\test_display_cache_governance.py -q

结果:13 个测试通过。

覆盖场景:

  • 本地 auto-created schema 中旧列保留且一致,display_cache_cleanup_prestate=ok。
  • 缺少核心表时,prestate 检查只 warning,核心表检查继续负责 error。
  • 存在 stale display cache 时,prestate 检查 warning。
  • 提前删除旧 UI 缓存列时,prestate 检查 error,并明确指出缺失列。

下一步

下一步仍按 Phase 3 小刀推进:先做 next_action 人工编辑入口的产品语义决策,然后进入停写 status_label/status_tone 的实现。不要直接删除旧列。

Slice 3.5.1 / 3.5.2 实施记录

本次继续沿 Phase 3 cleanup 路径收口,没有进入删列 migration。

next_action 语义确认

审计结果:

  • 前端合同编辑面板 apps/contract-web/src/app/contracts/[id]/contract-edit-panel.tsx 仍展示并提交 next_action 输入。
  • Contract API 的 ContractUpdateRequest 仍允许更新 next_action 与 next_actions。
  • 文件上传、审查任务、条款修复、审批实例和履约任务仍会影响 next_action/next_actions 的上下文派生。

结论:

  • next_action/next_actions 不能在本片作为纯 UI 缓存清理。
  • 后续若要删除旧列,必须先决定人工提示是否迁移为 action_hint 或 action override 结构。

status_label/status_tone 停写第一片

本次新增统一 helper:

  • sync_contract_status_display_cache(row)

语义:

  • 只根据 row.status 派生并写回旧兼容列 status_label/status_tone。
  • 不处理 next_action/next_actions。
  • 旧列仍保留,用于兼容、回滚和 schema-check。

已覆盖的生产 SQLAlchemy 写路径:

  • 合同创建:ContractCoreRepository.add_contract_in_session
  • 合同状态流:ContractAuditRepository.transition_contract / transition_contract_in_session
  • 审批提交、重提、通过、退回:经 SqlAlchemyTransitionOrchestration 统一进入派生写入
  • 归档:经同一 transition 路径派生写入
  • 发起审查:设置 status=reviewing 后统一派生写入

本片没有做:

  • 没有删除数据库列。
  • 没有修改 OpenAPI/前端响应字段。
  • 没有移除 next_action 前端编辑入口。
  • 没有重构 in-memory demo 仓储,因为本片目标是生产 SQLAlchemy 持久化路径停写。

本次验证

python -m compileall -q yuqei-contract-platform\services\contract-api\src\yuqei_contract_api
python -m pytest yuqei-contract-platform\services\contract-api\tests\test_contract_workflow.py yuqei-contract-platform\services\contract-api\tests\test_display_cache_governance.py yuqei-contract-platform\services\contract-api\tests\test_health.py -q

结果:通过。

新增回归:

  • test_contract_status_display_cache_is_derived_on_write_paths

验证点:

  • 真实审批流执行后,直接读取数据库行。
  • contracts.status_label 等于 status_label_for_status(row.status)。
  • contracts.status_tone 等于 status_tone_for_status(row.status)。

下一步

下一步建议先部署到远端 smoke 容器并复跑:

  • /api/v1/runtime/schema-check
  • 合同创建、提交审批、退回、重提、发起审查抽样
  • display_cache_cleanup_prestate

通过后再进入 next_action/action_hint 的语义设计,不要直接删列。

2026-07-01 prestate 重新固化

本记录中的早期 smoke 基线基于“状态默认派生值”检查。2026-07-01 排查详情页日志后,确认 cleanup prestate 必须与真实读路径一致,也就是使用完整 ContractActionContext。

最新 smoke 结论:

  • Alembic actual/head:20260701_0024
  • display_cache_consistency.checked_rows:12017
  • 完整上下文口径 backfill 前 mismatch:12006
  • 已执行 backfill apply:updated_rows=12006
  • 回滚 snapshot:/data/contract-files/display-cache-context-snapshot-20260701.json
  • backfill 后 display_cache_consistency.mismatch_count=0
  • display_cache_cleanup_prestate.status=ok
  • contract-api、contract-review-worker、contract-web 均为 healthy

同时,contract-api Docker healthcheck 已从重型 /api/v1/runtime/schema-check 调整为轻量 /api/v1/health。runtime/schema-check 仍是 smoke/CI 质量门,但不再作为 5 秒容器探针,避免 12017 行完整上下文检查导致健康状态误报。

详细记录见:

  • project-docs/V2-phase3-display-cache-context-prestate-2026-07-01.md