Files
ai-contract-platform/project-docs/V2-phase3-display-cache-context-prestate-2026-07-01.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

6.5 KiB
Raw Permalink Blame History

AI 合同平台 V2 Phase 3 display cache 完整上下文一致性收口

背景

本记录承接 Phase 3 UI 展示字段派生和旧 display cache 治理工作。

上一轮 smoke 中出现过一个容易误判的现象:

  • /api/v1/runtime/schema-check 中 display_cache_consistency.mismatch_count = 0。
  • 但合同详情页渲染期间,Contract API 日志仍出现 Contract display cache mismatch; derived value will be used。

排查后确认这不是单纯日志噪音,而是治理检查口径与真实读路径口径不一致。

根因

合同详情页、合同列表、workspace 和搜索读路径使用完整 ContractActionContext 派生展示字段。该上下文会纳入:

  • 最新审查任务状态。
  • 结构化风险项和条款修复状态。
  • 审批实例和待办步骤。
  • 履约任务。
  • action_hint。

旧的 display_cache_governance 只按 contracts.status 与旧缓存列做对比,没有加载上述上下文。因此会出现“schema-check 全绿,但详情页按完整上下文派生时仍 warning”的假一致。

本次修正

本次将 display cache 治理口径升级为完整上下文口径:

  • display_cache_governance.iter_display_cache_rows(...) 通过 SQL 读取最新审查、结构化风险、审批、履约和 action_hint。
  • display_cache_mismatch_for_row(...) 调用同一个 derive_contract_display(...),并传入 ContractActionContext。
  • schema_check 在缺少上下文表或 contracts.action_hint 时跳过 display cache assessment,避免旧库结构下误报连接异常。
  • contract-api Docker healthcheck 改为轻量 /api/v1/health。重型 /api/v1/runtime/schema-check 继续作为 smoke/CI 质量门,而不再作为 5 秒容器健康探针。

这修正了前一份 V2-phase3-search-action-context-audit-2026-06-30.md 中“display_cache_governance 保持轻量、不加载 workflow context”的阶段性判断。新的 smoke 证据表明:cleanup prestate 要判断旧缓存列是否真的安全,必须使用与用户可见读路径一致的完整上下文。

Smoke 验证

远端环境:

  • Host:192.168.3.90
  • Contract API:http://192.168.3.90:18100
  • Contract Web:http://192.168.3.90:13000
  • Database:yuqei_contract
  • Alembic actual/head:20260701_0024

修正治理口径后,首次复跑:

{
  "display_cache_consistency": "warning",
  "checked_rows": 12017,
  "mismatch_count": 12006,
  "field_counts": {
    "next_action": 12006,
    "next_actions": 12006
  }
}

抽样合同:

  • contract-0ba3bd5b04b6
  • 修复前旧缓存:根据审查结果修改或提交审批
  • 完整上下文派生:处理审查风险并应用推荐条款

这说明旧缓存主要不是状态标签问题,而是 next_action/next_actions 尚未按最新审查风险和条款推荐上下文刷新。

Backfill

已在远端 smoke 执行可回滚 backfill:

{
  "command": "backfill",
  "mode": "apply",
  "updated_rows": 12006,
  "snapshot": "/data/contract-files/display-cache-context-snapshot-20260701.json"
}

回滚 snapshot:

  • 容器路径:/data/contract-files/display-cache-context-snapshot-20260701.json
  • 宿主卷路径:/var/lib/docker/volumes/yuqei-contract-v2-smoke_contract-files/_data/display-cache-context-snapshot-20260701.json
  • 文件大小:约 5.0M

如需回滚 smoke 数据:

cd /opt/apps/ai-contract-platform-v2-smoke/yuqei-infra/compose
docker compose --env-file .env.smoke -p yuqei-contract-v2-smoke run --rm contract-api \
  python -m yuqei_contract_api.display_cache_governance_cli rollback \
  --apply \
  --snapshot /data/contract-files/display-cache-context-snapshot-20260701.json

Backfill 后结果

复跑 /api/v1/runtime/schema-check:

{
  "status": "ok",
  "display_cache_consistency": {
    "status": "ok",
    "checked_rows": 12017,
    "mismatch_count": 0
  },
  "display_cache_cleanup_prestate": {
    "status": "ok",
    "legacy_columns_retained": true,
    "display_cache_consistent": true
  }
}

复跑治理 CLI:

{
  "command": "assess",
  "checked_rows": 12017,
  "mismatch_count": 0,
  "field_counts": {},
  "samples": []
}

服务状态:

  • contract-api:healthy
  • contract-review-worker:healthy
  • contract-web:healthy

页面抽样:

  • /contracts/contract-0ba3bd5b04b6 返回 200。
  • 页面仍展示“可应用条款”推荐卡片。
  • 本次请求后 Contract display cache mismatch 日志计数为 0。

本地验证

python -m py_compile `
  yuqei-contract-platform\services\contract-api\src\yuqei_contract_api\display_cache_governance.py `
  yuqei-contract-platform\services\contract-api\src\yuqei_contract_api\schema_check.py

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

结果:

  • test_display_cache_governance.py:5 passed
  • test_health.py:10 passed

新增回归覆盖:

  • test_assess_display_cache_consistency_uses_review_action_context

该测试构造“合同状态默认缓存看似一致,但最新审查有未修复风险”的场景,确认治理检查会按完整上下文发现 next_action/next_actions mismatch。

当前结论

当前 smoke 已重新固化为:

  • 旧 display cache 列仍保留。
  • 旧列值已按完整业务上下文与 domain/serializer 派生结果一致。
  • display_cache_cleanup_prestate 可作为后续停写/删列前的保护门。
  • 不建议立即删列。

下一步建议

下一片仍应走保守路径:

  1. 先继续停写旧 contracts.next_action / contracts.next_actions 的生产写路径。
  2. 保留 API 响应字段由 derive_contract_display(...) 输出。
  3. 重建搜索索引或增量同步时继续使用完整 ContractActionContext。
  4. 连续 smoke 多轮保持 display_cache_consistency=ok 后,再评审是否进入 cleanup migration。

不要把 status_label/status_tone 与 next_action/next_actions 一次性删掉。前者是纯展示缓存,后者仍承担动作上下文兼容面,必须分片处理。

后续停写进展

2026-07-01 后续小片已开始停写旧 contracts.next_action / contracts.next_actions 生产写路径。

新的口径是:

  • status_label/status_tone 继续作为兼容缓存保持一致。
  • next_action/next_actions 旧列仍保留,但不再要求跟随完整上下文派生值写回。
  • API 响应和搜索展示继续由完整 ContractActionContext 派生。

详细记录见:

  • project-docs/V2-phase3-stop-legacy-next-action-writes-2026-07-01.md