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>
7.2 KiB
7.2 KiB
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_repairsource/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 返回结构化冲突
- goose
- 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 buildPASS - 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清空活动文档时必须 bumphydrateResumeKey;编辑中保护草稿并requestHydrationResume。setDeliverablesupdater 不再嵌套 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。
集成顺序(红线)
- 合
dev+ rebuild(schema 含 00032) - live dry-run 确认 class=
LEGACY_CANONICALIZATION_DRIFT - live apply
doc_proj143_intake - 然后才做 W8 页面验收
未 repair 前打开文档应看到结构化 drift 冲突,而不是裁掉双空格的旧 head。
风险 / Blocker
- 隔离 PG 未跑:不能宣称真实 PG apply 已验证。W7 前用隔离库补跑
TestReconcile_LegacyRepair_PG - 代码合入前,项目 143 现场仍失败(旧镜像 + 未 apply)
- apply 后仍须 W8 页面验收才能把 gap 标 PASS