test+docs: harden and document the historical-import preserveAudit seam (#3493 follow-ups) - #3560
Merged
Merged
Conversation
…am (#3493 follow-ups) Follow-ups to #3493 / #3549 / #3556 — no behavior change. Real-SQLite end-to-end test (@objectstack/runtime): wires the real ObjectQL engine to the real SqlDriver (better-sqlite3) and proves the preserveAudit seam that mock / in-memory drivers structurally cannot — that context.preserveAudit threads through buildDriverOptions and defeats the SQL driver's updated_at force-stamp on both the forward historical write and the undo restore, and that a business readonly field (closed_at) survives the engine strip all the way to disk. Includes the #3549 undo capstone: restore-under-preserveAudit rolls the timeline back, while a plain restore re-stamps now (the bug #3556 fixed). Docs: the treatAsHistorical schema describe (@objectstack/spec, regenerated references/api/export.mdx) now documents all three effects — FSM skip (#3479), audit-timeline preservation (#3493) and undo mirroring (#3556) — instead of only the state-machine half; protocol/objectql/state-machine.mdx gains a bullet on the symmetric undo behavior. Also confirms (via a codebase audit) that the import-undo route was the only business-record snapshot write-back path, so #3556 needs no sibling fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B5rdfBKjkbcoEif4KUV6xE
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 111 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
July 27, 2026 05:48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
这是 #3493 / #3549 / #3556 的三项收尾(你说「按照我的建议继续完成所有」)。无行为改动——一条端到端测试 + 文档补全 + 一次对称性巡检结论。
1. 对称性巡检(结论:无需再修)
按建议先做了一次全仓巡检:排查是否还有其他「写回既有记录的历史快照」的路径遗漏了
preserveAudit(即 #3549 的同类缺陷)。结论:导入撤销路由是唯一一条会写回业务记录「捕获快照 + 审计/业务 readonly 字段」的路径,而它已在 #3556 正确修复(按
treat_as_historical条件带preserveAudit)。其余近亲路径都不是同类缺陷:restoreVersion/revertCommit/rollbackToPackageCommit):把旧的定义体写回 JSON 列,同时有意记一条全新的operation_type='revert'append-only 事件——这里保留旧updated_at反而是错的,与 data import undo: 撤销一次「historical」导入无法还原被保留的 readonly / 审计字段 — undo 的 writeCtx 缺 preserveAudit (#3493 follow-up) #3549 的诉求相反。isSystem/seedReplay但不带preserveAudit。因为 seed 是静态定义、没有「被捕获的真实时间线」,当前无缺陷;仅当 seed 语义将来改为携带权威历史时间戳时才需要补preserveAudit——已在下方留档,不作为本 PR 的改动。2. Real-SQLite 端到端测试(
@objectstack/runtime)packages/runtime/src/preserve-audit-real-driver.integration.test.ts——真实ObjectQL引擎 + 真实SqlDriver(better-sqlite3,落盘),读回持久化行断言。补上了 mock / 内存驱动结构上无法证明的那道缝:
context.preserveAudit穿过buildDriverOptions进入驱动 options,击穿真实 SQL 驱动对updated_at的强制 stamp。三条用例:updated_at与业务 readonly 字段closed_at,一路写到磁盘。updated_at(≠ 历史值),引擎 strip 掉closed_at(从不落库)。updated_at回滚到原值;不带 flag 的同样还原 → 被强制 stamp 成「现在」(正是 fix(rest): undo of a historical import preserves the audit timeline (#3549) #3556 修掉的破坏)。3. 文档补全
treatAsHistorical的 schema describe(@objectstack/spec→ 重新生成references/api/export.mdx):原文只写了跳过状态机那一半(data import: mid-lifecycle rows rejected by state_machine.initialStates — historical-data migration blocked (#3433 sibling) #3479),现在补齐三条效果——跳 FSM(data import: mid-lifecycle rows rejected by state_machine.initialStates — historical-data migration blocked (#3433 sibling) #3479)、保留审计时间线(data import: a "historical" import can't preserve original timestamps / audit fields — updated_at is stamped now, readonly fields stripped on upsert (#3479 follow-up) #3493)、撤销镜像(fix(rest): undo of a historical import preserves the audit timeline (#3549) #3556)。protocol/objectql/state-machine.mdx:新增一条关于「撤销历史导入是对称的」的说明(data import undo: 撤销一次「historical」导入无法还原被保留的 readonly / 审计字段 — undo 的 writeCtx 缺 preserveAudit (#3493 follow-up) #3549/fix(rest): undo of a historical import preserves the audit timeline (#3549) #3556)。校验
check:docs✅(253 生成文件 in sync)check:api-surface✅(describe 改动不影响 API surface)check:doc-authoring✅(213 文件 clean)Test Files 1 passed / Tests 3 passedChangeset
空 frontmatter「releases nothing」——describe 字符串 + 重新生成的文档 + 一条新测试,无任何已发布包的行为改动。
🤖 Generated with Claude Code
https://claude.ai/code/session_01B5rdfBKjkbcoEif4KUV6xE
Generated by Claude Code