Files
Agent-System/.doc/ops/handoffs/2026-09-14-controlled-content-canonical-hash.md
T
weichenandCursor 08bf615f95 fix(web): keep hydrated content across late list sync
Hard refresh wiped cache/ready after a successful hydrate, then the
unchanged fingerprint never re-ran the effect. Invalidate only via a
planner that preserves list-sync hits and re-hydrates after post-task drops.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-14 03:33:37 +00:00

7.2 KiB
Raw Blame History

title, created, from, to, status
title created from to status
受控正文 canonical hash 漂移 W1–W6 + 审查修补交接 2026-09-14 implementer_cursor orchestrator_codex / W7 holder done

交接:受控正文 canonical hash 漂移

目标(下一位 Agent 应完成什么)

占 CURRENT 集成槽后将 fix/controlled-content-canonical-hash --no-ff 合入 dev,env-service.sh dev rebuild,先对项目 143 完成 live dry-run + apply,再做 W8 真实 UI。合入后若未 repair 就打开页面,content API 会返回 DOCUMENT_CONTENT_PROJECTION_DRIFT,而不会静默展示裁掉硬换行的旧 head。

已完成

  • W1–W6 工程实现与门禁
  • 合入前审查修补:
    • goose 00032 允许 migration_repair source/change kind
    • migration_repair 不递增 head_revision_no / human_revision_count;ETag 随新 snapshot ID 变化
    • CLI 显式 live 模式 + /app/data → host data root 映射
    • MarkDocumentContentUpdated 未预加载时从 DB 装入;aligned/retry 仍同步记录哈希
    • 读路径对 legacy/fork 返回结构化冲突
  • follow-up:apply 同时备份原文件与旧 head 正文(head.md)及 metadata;唯一目录 + O_EXCL;原子写 rename 后打开父目录 Sync(EINVAL/ENOTSUP/ENOSYS 忽略)
  • 终审:--live --environment dev 必须同时识别 DSN :5433 与库名 agent_system_dev,且 --host-data-root 为 data/dev;空 dsnEnv/pathEnv 不放行;stored work_dir 必须在 --stored-data-root 下并映射,禁止沿用其他绝对路径
  • 工程门禁(worktree):go vet、go test -short、web npm test、web npm run build PASS
  • UI follow-up:shouldLoadControlledContent / hydrateControlledDocumentContent 与 revisionToolbarMountMode 的 server-backed 资格对齐;doc-001 等本地 legacy/mock 不再请求 content 与 revision-state。doc_proj* 仍 hydrate;已注册 doc-free/freeform 与 template preview 不走受控 content 请求
  • UI 竞态 follow-up:planControlledContentInvalidation 统一失效。list_sync 不再清空 cache/ready(硬刷新后晚到列表不能把刚成功的 hydrate 打回 loading);post_task 清空活动文档时必须 bump hydrateResumeKey;编辑中保护草稿并 requestHydrationResume。setDeliverables updater 不再嵌套 setCache/setReady

未完成

  • W7 合 dev + 容器 rebuild(本 worktree 禁止)
  • 隔离 TEST_DATABASE_URL 上实际跑 TestReconcile_LegacyRepair_PG:本机未配置隔离库;5432/5433/5434/5435 为共享环境,已拒绝使用。这是 apply 可落地的工程 blocker,不是业务库可替代项
  • 未在真实 data/dev / 共享库执行 apply
  • W8 真实 UI(须在 143 repair 之后)

认领 / 槽位状态

项 值
mutex 已释放 yes(docgov done;合入前再占)
集成槽位 holder idle
branch fix/controlled-content-canonical-hash
worktree .worktrees/fix-controlled-content-canonical-hash

合 dev 必须另占集成槽位。W7 前请重新登记 docgov。

关键文件

路径 说明
deploy/postgres/goose/00032_2026-09-14-document-revision-migration-repair.sql 约束允许 migration_repair
internal/fsutil/atomic.go rename 后打开父目录 Sync;O_EXCL;不支持的 dir sync 忽略
internal/docrevision/reconcile.go 唯一备份目录;回报 output/head/meta 三条路径
cmd/document-content-reconcile/safety.go live 正向确认 dev DSN 与 data/dev
web/src/lib/controlledDocHydrate.ts 本地 legacy/mock 不 hydrate;与 toolbar 资格一致
web/src/lib/controlledContentInvalidation.ts 受控正文失效/重水合规划;list_sync 保 hydrate,post_task 必重拉
internal/docgov/engine.go Mark 预加载/幂等补偿
cmd/document-content-reconcile/ 默认拒绝共享;live 硬门闩 + 路径映射
internal/server/project_doc_handlers.go drift 返回 DOCUMENT_CONTENT_PROJECTION_DRIFT

如何验证

go vet ./...
go test -short ./...
go test ./internal/docrevision/ ./internal/docgov/ ./internal/server/ ./internal/db/ ./cmd/document-content-reconcile/ \
  -run 'TestCanonicalize|TestClassify|TestReconcile|TestCommitMigrationRepair|TestMapStored|TestInspectProjection|TestMigration032|TestReadDocumentContent_Typed|TestWriteControlledContentError|TestServeLedgerHead|TestValidateCLI'
cd web && npm test && npm run build
# 重点:controlledDocHydrate 拒绝 doc-001 / template / registered freeform 发 content+revision
# 重点:controlledContentInvalidation 覆盖 hydrate→晚到sync、post_task 重水合、编辑/PATCH 防旧 GET

隔离 PG(禁止 5433/5434/5435 与 agent_system_dev|test|main):

TEST_DATABASE_URL='postgres://app:pass@127.0.0.1:55432/agent_itest?sslmode=disable' \
  go test ./internal/docrevision/ -count=1 -run TestReconcile_LegacyRepair_PG

数据迁移命令的 dry-run / apply 安全边界

默认拒绝 :5433|:5434|:5435 与 data/dev|test|main。

隔离库(无 --live)可 dry-run / apply,仍须 --backup-dir。

真实 dev 单文档必须同时具备:

  • --live
  • --environment dev
  • 精确 --doc-id(禁止批量)
  • apply 时 --confirm REPAIR_SINGLE_DEV_DOC 与 --backup-dir

宿主路径映射(DB 中 work_dir=/app/data/...):

# dry-run(live,无 confirm)
go run ./cmd/document-content-reconcile \
  --dsn "$DEV_DSN" \
  --live --environment dev \
  --doc-id doc_proj143_intake \
  --stored-data-root /app/data \
  --host-data-root "$REPO/data/dev" \
  --report /tmp/proj143-reconcile-dry-run.json
# DSN 必须是 :5433 / agent_system_dev;host-data-root 必须就是 data/dev

# apply
go run ./cmd/document-content-reconcile \
  --dsn "$DEV_DSN" \
  --live --environment dev \
  --doc-id doc_proj143_intake \
  --confirm REPAIR_SINGLE_DEV_DOC \
  --stored-data-root /app/data \
  --host-data-root "$REPO/data/dev" \
  --apply --backup-dir /safe/backup/proj143 \
  --report /tmp/proj143-reconcile-apply.json

容器内可执行:在已 rebuild 的 shuimu-agent-app-dev 中运行同一 CLI。/app/data 已挂载时可不做 host 映射,但仍须 live 门闩与单 --doc-id。

Apply 只处理 LEGACY_CANONICALIZATION_DRIFT:备份 + migration_repair,不覆盖原文件,不只改 DB hash。

集成顺序(红线)

  1. 合 dev + rebuild(schema 含 00032)
  2. live dry-run 确认 class=LEGACY_CANONICALIZATION_DRIFT
  3. live apply doc_proj143_intake
  4. 然后才做 W8 页面验收

未 repair 前打开文档应看到结构化 drift 冲突,而不是裁掉双空格的旧 head。

风险 / Blocker

  • 隔离 PG 未跑:不能宣称真实 PG apply 已验证。W7 前用隔离库补跑 TestReconcile_LegacyRepair_PG
  • 代码合入前,项目 143 现场仍失败(旧镜像 + 未 apply)
  • apply 后仍须 W8 页面验收才能把 gap 标 PASS

相关链接