Skip to content

refactor(service-storage): 摘除 Local/S3 适配器的 list(prefix) 实现与测试 (#5541) - #6061

Merged
hotlong merged 2 commits into
mainfrom
claude/issue-5541-storage-list-impl-removal
Aug 7, 2026
Merged

refactor(service-storage): 摘除 Local/S3 适配器的 list(prefix) 实现与测试 (#5541)#6061
hotlong merged 2 commits into
mainfrom
claude/issue-5541-storage-list-impl-removal

Conversation

@hotlong

@hotlong hotlong commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #5541

#5540 摘掉了 IStorageService.list?(prefix) 这个契约成员;本 PR 是它的实现半 —— 摘掉两个出厂适配器自己的 list 实现、它们的测试,以及各自 track() 指标词表里那个再也产不出来的 'list' 标签。

范围核对(⛔ 不重做 PR #5983 已完成的部分)

开工第一步读了 efedd28(PR #5983)的终态 diff。它已原子落地:spec 契约成员 + SwappableStorageService.list 代理透传 + 该文件两处测试 + 文档 + ADR-0087 D3 登记。这些一律没有再动swappable-storage-service.ts:74-76 留的指向本单的注释也保持原样。

本 PR 只做剩下的四处(两个生产者 + 它们的测试):

文件 改动
local-storage-adapter.ts list(单层 readdir、把子目录当文件返回);track() 词表去掉 'list'
s3-storage-adapter.ts list(单发 ListObjectsV2,IsTruncated / ContinuationToken 都不读,过 1000 个对象静默截断);track() 词表去掉 'list'
local-storage-adapter.test.ts 退役两条「列目录」用例 —— 它们钉的正是被删的语义(两个 fixture 都恰好在一层深,那是该实现唯一看得见的深度),留着只能靠保留方法才绿
local-storage-adapter.metrics.test.ts 一条用例去掉 list 断言;list() does not double-count head per entry 整条退役(它钉的是被删实现的内部细节)
swappable-storage-service.test.ts FakeAdapter.list —— #5983 之后它已无人调用,是个「真适配器都没有的表面」的假体残留
storage-adapter-list-retirement.test.ts(新增) 留钉:断言两个适配器都不再暴露 list

零消费方证据(全仓引用面)

摘除前后同一条查询,packages/services/service-storage/ 内:

# origin/main(改前)
$ git grep -n "\.list(" origin/main -- packages/services/service-storage/
local-storage-adapter.metrics.test.ts:39:  await storage.list('a');
local-storage-adapter.metrics.test.ts:52:  await storage.list('p');
local-storage-adapter.test.ts:81:          const files = await adapter.list('docs');
local-storage-adapter.test.ts:89:          const files = await adapter.list('nonexistent');

# HEAD(改后)—— 同一条查询
$ git grep -n "\.list(" HEAD -- packages/services/service-storage/
(no matches)

改前的四处全部是测试,没有一处产品代码调用 —— 这与 #5266 的分析一致:唯一的仓内调用点是 #5983 已删掉的代理透传。跨包声明式 + 带引号精确名两种查法(storage.list( / storageService.list( / adapter.list( / async list( / list?(prefix)在 packages/ examples/ apps/ scripts/ 全仓扫过,除 packages/spec 的退役登记文本外,命中的 async list(...) 全是同名不同物:metadata loader 的 list(type)、messaging outbox 的 list(filter)suspended-run-storelist(),与存储无关。存储侧零残余。

必答项:StorageFileInfo 是否随之退役?

不退役 —— 它仍有消费方,list 从来不是它唯一的生产者。 实测:

所以 changeset 措辞按「成员摘除、类型保留」写,没有任何类型退役的语句。

反向验证(方向先声明,再跑)

预判写在动手之前:把 list 加回两个适配器,新留钉应变红,而包的 build(tsup DTS,即 tsc)应保持绿。后半句才是这条钉子存在的理由 —— class 携带接口未声明的多余成员不是类型错误,这正是 #5540 changeset 对适配器作者的承诺原文(「an implementation left in place still compiles」),所以 tsc 守不住这条线。

两半都如预判:

# 加回 list 后 —— 留钉变红(两个适配器各一条)
FAIL src/storage-adapter-list-retirement.test.ts > LocalStorageAdapter exposes no list member
  AssertionError: expected [ 'rootDir', 'partsDir', …(23) ] to not include 'list'
FAIL src/storage-adapter-list-retirement.test.ts > S3StorageAdapter exposes no list member
  AssertionError: expected [ 'options', 'clientPromise', …(25) ] to not include 'list'
  Tests  2 failed | 1 passed (3)

# 同一状态下 —— build 依旧绿,tsc 一声不吭
$ pnpm --filter @objectstack/service-storage build   →  BUILD_EXIT=0
  DTS ⚡️ Build success in 5130ms

随后已还原,git diff --stat 与还原前逐字节一致。留钉里第三条用例(仍暴露 upload/download/delete/exists/getInfo)是防镜像失效的:一个「适配器什么方法都没有」的空壳同样会让前两条绿。

验证

门是从 .github/workflows/lint.yml 逐条枚举跑的,不是凭记忆挑的。

  • pnpm --filter @objectstack/service-storage test22 files / 282 tests passed(含新留钉 3 条)
  • pnpm --filter @objectstack/service-storage build → DTS 成功(该包无 typecheck script,tsc 走 tsup DTS 这条道,与 refactor(spec)!: 按 ADR-0049 摘除 IStorageService.list(prefix) —— 零消费方、双适配器语义分叉 (#5540) #5983 commit message 记录的一致)
  • turbo run build(service-storage + 全部三个下游 cli / plugin-dev / dogfood 及其依赖闭包)→ 62 tasks successful,证明删除对下游零类型影响
  • lint job 全部 root 门 + typecheck job 非 spec 门:lint(ESLint)、check:slot-lookupcheck:query-options-erasurecheck:nul-bytescheck:doc-authoringcheck:docs-audit-scopecheck:role-wordcheck:adr-anchorscheck:org-identifiercheck:authz-resolvercheck:service-providerscheck:route-envelopecheck:error-code-casingcheck:wildcard-fallthroughcheck:init-service-contractcheck:durability-log-levelcheck:startup-registry-verdictcheck:objectui-changesetcheck:release-notescheck:release-bodycheck:node-versioncheck:workflow-status-functionscheck:published-filescheck:engine-double-contractcheck:resume-authority-declaredcheck:type-check-coveragecheck:driver-conformancecheck:stall-guardcheck:skill-frame-synccheck:i18ncheck:i18n-coverage全 PASS
  • ⛔ 未触 packages/spec,未重生成任何 spec 产物(git status -- packages/spec content/docs docs/ 为空)

changeset

.changeset/storage-adapter-list-implementations-removed.md,@objectstack/service-storage: patch。按约束写得响亮:两个方言各自错在哪、该成员从未有过仓内消费方、以及未来按需回归的形状指引 —— 回来时是带游标的 list(prefix, { cursor, limit }),返回一页 + 续传 token,并要求 nested keys / 目录项 / 超 1000 对象三类 adapter-conformance 用例证明两个后端答案一致,不继承坏签名(#5266 维护者裁决原文)。取 patch 而非 major:契约破坏是 #5540 的、已在那里发布;这里同时如实列出直接持有具体类 LocalStorageAdapter / S3StorageAdapter.list() 的下游该改成什么,没有藏。


Generated by Claude Code

The implementation half of the #5540 contract retirement (ADR-0049
enforce-or-remove; analysis #5266). #5983 landed the spec member and the
SwappableStorageService passthrough atomically; this removes what it left:

- `LocalStorageAdapter.list` (single-level readdir, directories returned as
  files) and `S3StorageAdapter.list` (recursive ListObjectsV2, silently
  truncated at 1000 objects, IsTruncated/ContinuationToken never read);
- the `'list'` label in each adapter's private `track()` metrics vocabulary,
  which no site can produce anymore;
- the tests that pinned those two dialects, plus the dead `FakeAdapter.list`
  in the swappable proxy's test.

Zero in-repo consumers: after #5983 the only surviving references were the
two producers and their own tests.

The absence is held by a new runtime pin, `storage-adapter-list-retirement.test.ts`.
tsc cannot hold this line -- a class may carry members its interface does not
declare, which is exactly what the #5540 changeset promised adapter authors --
verified by restoring both methods: the pin goes red on both adapters while
`pnpm --filter @objectstack/service-storage build` (tsup DTS, i.e. tsc) still
exits 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015a5qkLzpGXhLL2F5gvJ7dD
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 7, 2026 12:24am

Request Review

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/service-storage.

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

  • content/docs/api/plugin-endpoints.mdx (via @objectstack/service-storage)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/service-storage)
  • content/docs/plugins/packages.mdx (via @objectstack/service-storage)
  • content/docs/releases/implementation-status.mdx (via @objectstack/service-storage)

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.

attempt-3 重投(run 31118691490/31118691377/31118691509)在平台故障期间
发起,恢复后 4 小时仍无 runner,且 API 拒绝取消:
「Cannot cancel a workflow re-run that has not yet queued」——重投请求
卡死在预入队状态,平台侧不可解。同期新建 run 秒级拿 runner,证明队列
本身健康。换头 SHA 让全套 check 全新起跑。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015a5qkLzpGXhLL2F5gvJ7dD
@hotlong
hotlong marked this pull request as ready for review August 7, 2026 00:50
@hotlong
hotlong added this pull request to the merge queue Aug 7, 2026
@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

队列管家:本 PR 是链上连坐**,自身无问题 ⇒ ⛔ 未重投、无需改动**

本 PR 所在的队列世代 Lint \& Type Check 判红,致命 step 9 Slot-lookup ratchet:

• packages/plugins/plugin-sharing/src/sharing-plugin.ts: erasure count grew 10 → 11.

该文件不在本 PR 的改动面内。 本轮以 base sha 重建队列链序为 #5999#6067#6086#6061#6068;grouping_strategy: ALLGREEN 下,链上条目会继承其前序条目的改动。改动 sharing-plugin.ts 的是链上第 2 位的 #6067(新增 ctx.getService<any>('tenancy'),即 ratchet 所数的类型擦除站点),本 PR 位于其之后,故继承该红。

反证:链上位于 #6067 之前#5999 没有这条红(它的红是另一条已知 flaky,在 CI job 而非 Lint job)⇒ 责任边界与链序一致。

处置:⛔ 不重投(重投无效——只要 #6067 仍在本 PR 之前,该红就会重现)、⛔ 不改代码。#6067 转绿或出队后本 PR 自动恢复。完整签名与修法建议已留在 #6067 的拦截评论

已核让行:本 PR 最近 30 分钟无车道 PM 动作;本 PR 当前仍在队列内(无 removed 事件),本座位未撤队、未动认领。


Generated by Claude Code

Merged via the queue into main with commit 1f1edc0 Aug 7, 2026
24 checks passed
@hotlong
hotlong deleted the claude/issue-5541-storage-list-impl-removal branch August 7, 2026 01:13
os-zhuang pushed a commit that referenced this pull request Aug 7, 2026
合并 main 到 dca5bd3 后再全量重测,余量在一小时内被兑付了两笔,记录如下:

- `@objectstack/objectql` 实测 339 -> **345**(+6 全是 TS2554,全在
  `src/summary-rollup.test.ts`,由飞行途中落地的 #5749 / PR #6013 扩写)。
  记档 349 把它静默吸收了 —— 若按精确值 339 记账,这就是同一场赛跑的第 6 次红。
  按裁决「实测 +10」把记录抬到 **355**,恢复满额余量。
- `@objectstack/service-storage` 42 -> 41 -> **42**:`IStorageService.list(prefix)`
  的退休被拆成两个 PR,spec 半边(#5540 / PR #5983)减 1、适配器半边
  (#5541 / PR #6061)删旧测试(-1 TS7006)又新增
  `storage-adapter-list-retirement.test.ts`(+2 TS2835),净 +1。上一轮我按实测
  下调到 41,一小时后就被咬红 —— 正是派发令说的「非余量条目被基漂移咬住」,
  按同一记档规则给这条加 +10,记 **52**,不开精确校准 lap。

一个值得写进文档块的新形状:**拆成两个 PR 的退休会让计数先降后升**,在两半之间
记下的精确值,推上去之前就已经过期。

`rest` / `lint` 两条实测未动(153 / 32),余量原样,note 补记「一小时后在
77c7c88 复测仍是该值」。

重测输出:四条记档余量各打印一行 ℹ(各 -10),无一条上漂,exit 0。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

service-storage: 摘除 Local/S3 适配器与 SwappableStorageService 的 list 实现及测试(#5266 方案 2 的实现半,随 spec 契约摘除)

2 participants