Skip to content

feat(storage): 开启回收 — 释放的字段文件按部署门禁进入墓碑与回收 — ADR-0104 D3 wave 2 (PR-5b) (#3459) - #4208

Merged
os-zhuang merged 1 commit into
mainfrom
claude/adr-0104-d3-wave2-eval-2v9jsy
Jul 30, 2026
Merged

feat(storage): 开启回收 — 释放的字段文件按部署门禁进入墓碑与回收 — ADR-0104 D3 wave 2 (PR-5b) (#3459)#4208
os-zhuang merged 1 commit into
mainfrom
claude/adr-0104-d3-wave2-eval-2v9jsy

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

wave 2 的最后一步,#3459 唯一剩余项。Draft:合并需要明确的人工决定(issue 原文要求),请在确认 v17 RC 时间表后再转 ready。

两半,同一个 change(#3459 的硬约束 1)

  1. release 侧打墓碑releaseOwnership 在清所有权列的同时,把已释放的 committed 文件置为 status='deleted' + deleted_at,进入 sys_file lifecycle 声明的 30 天宽限窗——仅当本部署的 adr-0104-file-references 标记已 verifiedos migrate files-to-references --apply平台形态的迁移门禁:带自检的数据迁移 + 部署级标记 —— file-as-reference 回收与 strict 翻转都依赖它 #3617)。窗内重新引用该 id 会复活它(claim 路径已有的 un-tombstone)。
  2. reap guard 扫描时复核扩展到所有权列:墓碑行只要 ref_* 列记着现任所有者(窗内被重新 claim、或 release/claim 竞态)→ un-tombstone + veto,与附件重获 join 行同待遇。此前 guard 只复核 sys_attachment——对字段文件恒为空,这正是"只做一半 = 每次释放都是保证性字节删除"的机制,现在由两侧的回归测试锁死。

门禁读取的不对称,是故意的

位置读法理由
release 侧(热路径)engine 的 memoized 读取(isFileReferencesMigrationVerified() 公开化,duck-type 可选方法接入)每进程一次查询;方向安全——过期的"未验证"只是少打墓碑
sweep 侧(不可逆时刻)每批 fresh 读取(plugin 注入回调)数据回归(后续失败的迁移运行清空 verified_at)要能不重启就拦住已打好的墓碑

一切"不知道"——没有标记行、表不可读、engine 没有该方法(旧 engine、测试 fake)、回调抛错——一律读作"未验证":门禁 fail closed,倒向保留。gate 关闭时 veto 但不 un-tombstone:观察到的释放事实成立,被扣下的只是删除许可。

未迁移的部署,行为一个字节都不变

release 只清所有权列,文件继续永生。attachments scope 的回收不依赖此标记,行为不变。升级不是同意;跑通自己迁移的自检才是——真正不可逆的时刻是各部署 verified 后释放文件的第 30 天,不是装上本版本那天。

测试

  • file-reference-lifecycle.test.ts:R4 回归测试按新不变量改写(fail closed 三形态:无方法 / 未验证 / 读取抛错 → 永不墓碑;已验证 → 删除与改写字段两条路径都墓碑;pending 文件即使已验证也不墓碑)。
  • attachment-lifecycle.test.ts +7:gate 开启时回收字段文件、所有权列复核锁(regained owner → un-tombstone + veto)、无回调 / 回调 false / 回调抛错三种 veto、gate 读取抛错不阻断同批附件回收、每批只读一次 flag。
  • 全量:service-storage 252 ✅ · objectql 1238 ✅ · platform-objects + cli ✅ · 两轮全量 pnpm test 并集全绿(各有一个互不相同、与本改动无关的超时 flake:plugin-audit、cloud-connection 的 marketplace 用例,均单独复跑通过)· lint ✅ · nul-bytes ✅ · doc-authoring ✅ · release-notes ✅

文档与 changeset

  • cli.mdx「What the flag turns on」表格:回收行为从"Not yet shipped"改为实际语义;重启说明补充 sweep 侧 fresh 读取的例外。
  • v17.mdx:released-file collection 一节(升级不是同意 + 30 天窗口)。
  • changeset ×2:service-storage minor(主叙事,含"两半必须同行"的约束记录)、objectql minor(engine 方法公开化)。

后续(不在本 PR)

Closes#3459 的 PR-5b 项。

🤖 Generated with Claude Code

https://claude.ai/code/session_01KM46HedAWLbkuZyMBXn5wJ


Generated by Claude Code

…eployment migration gate — ADR-0104 D3 wave 2 PR-5b (#3459)
The gated final step of the file-as-reference sequence, both halves in one
change as #3459 requires:
- releaseOwnership tombstones a released committed file (status='deleted' +
deleted_at, starting the declared 30-day grace window) — but only on a
deployment whose adr-0104-file-references flag is verified, read through
the engine's memoized seam (now public), failing closed every way of not
knowing.
- The sys_file reap guard's sweep-time re-verify extends to the ownership
columns (a re-claimed file is un-tombstoned and vetoed) and re-reads the
deployment flag fresh before any byte delete, so a regressed gate closes
for already-written tombstones without a restart. Attachments-scope
collection is unchanged and needs no flag.
Unmigrated deployments keep today's behaviour exactly: release clears the
ownership columns and files are retained forever. The R4 regression tests
flip to the new invariant and lock the two halves together.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KM46HedAWLbkuZyMBXn5wJ
@vercel

vercelBot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredJul 30, 2026 3:19pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/objectql, packages/services.

19 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/webhooks.mdx(via packages/services)
  • content/docs/concepts/metadata-lifecycle.mdx(via @objectstack/objectql)
  • content/docs/data-modeling/formulas.mdx(via packages/objectql)
  • content/docs/deployment/migration-from-objectql.mdx(via @objectstack/objectql)
  • content/docs/deployment/vercel.mdx(via @objectstack/objectql)
  • content/docs/kernel/runtime-services/audit-service.mdx(via packages/services)
  • content/docs/kernel/runtime-services/index.mdx(via packages/services)
  • content/docs/kernel/runtime-services/settings-service.mdx(via packages/services)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/objectql)
  • content/docs/kernel/services.mdx(via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx(via @objectstack/objectql)
  • content/docs/plugins/index.mdx(via @objectstack/objectql)
  • content/docs/plugins/packages.mdx(via @objectstack/objectql, packages/services)
  • content/docs/protocol/kernel/i18n-standard.mdx(via packages/services)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/objectql)
  • content/docs/protocol/objectql/query-syntax.mdx(via packages/objectql)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/objectql)
  • content/docs/releases/implementation-status.mdx(via @objectstack/objectql)
  • content/docs/releases/v9.mdx(via @objectstack/objectql)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ADR-0104 D3 wave 2 — file-as-reference:独占所有权模型 + 到开启回收的分步序列(PR-5b 门禁改由 #3617 承载)

2 participants

@os-zhuang@claude