refactor(spec)!: ADR-0049 退役 sweep —— server 运行期词表 / ViewProtocol / L2 ETL(#5295 #6239 #6414) - #6526
Conversation
…y, ViewProtocol, L2 ETL (#5295 #6239 #6414) 三个成员共用 route 3(整 def 删除):都不是 authorable 键、全仓零 parse, 因此无 retiredKey() 墓碑、无 D2 conversion —— `RETIRED_DEFS_BY_MAJOR[17]` (23 defs) 加三条 D3 `SemanticMigration` 即声明本身。 - #5295 `system/http-server.zod.ts` 的运行期词表(ServerEvent* / ServerCapabilities / ServerStatus)。前置测量:plugin-hono-server 既不实现 也不报告三者;对照组 MiddlewareConfig 在同一轮扫描中命中 runtime 消费者。 - #6239 `ViewProtocol` 五方法与十套 Request/Response schema。零实现、零路由、 零调用方;#5948 曾把 GetViewResponseSchema 误当 /ui/view 的契约。 - #6414 整个 L2 ETL 层。零执行侧消费者、无 liveness 台账;同 PR 重写 `packages/spec/docs/SYNC_ARCHITECTURE.md`(它此前把 ETLPipeline 当作 L1 退役后的推荐去处),并吸收 #4962 的 etl-retry 语义迁移条目。 生成基线整体重生成;ADR-0122 pin 751 → 744(收据写在计数断言处)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011M7UwH25Unfi73UHim7ajY
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
manifest 相对 origin/main 的删除恰为本 sweep 的 23 个 def,零新增; #6474 的 ValidateData 等同期条目在重生成后完整存活。
📓 Docs Drift CheckThis PR changes 2 package(s): 115 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
与 main 同步:os-regen 四步已走完(正文两处读数随之更新)
手工消解两处冲突,均非生成物:
|
- 三份 changeset 各补 `adr-0087: registered <id>`(#6148 门禁要求书面处置) - `integration/connector.zod.ts` 的三处 TSDoc 指向 automation/etl.zod.ts: L2 已退役,指针改指真正执行的面(data/mapping.zod.ts 的 transform), 而不是转到另一个不执行的层——这正是该文件 header 自己写的"signpost 必须 落在被执行的地方" - quick-reference.mdx 删掉 ETL 行并把 Automation 计数 5 → 4
- content/docs/references/integration/connector.mdx 随 connector.zod.ts 的 TSDoc 修正重生成(三条死链消失) - alias-integrity.test.ts 的十点测量里 etl.zod.ts 已不在:保留原计数并注明 退役,论点是 AST 盲区,不因少一个文件而变弱
CI 收敛:
|
| job | conclusion |
|---|---|
| ESLint | success |
| TypeScript Type Check | success |
其余:CI / Lint & Type Check / Check Changeset / Check Documentation Links / Spec property liveness / Test Core (1-3/3) / Dogfood Regression Gate (1-3/3) / Dogfood Verify CLI / Temporal Conformance (live PG + MySQL) / Build Core / Build Docs / Check PR Size / Auto Label / Console Pin Freshness / Flag docs affected by code changes / No other open PR may claim the same issue / filter / Vercel Preview Comments —— 全部 success;Console Pin Gate skipped。非绿:零。
这条 SHA 上首轮就全绿,不是「打完标签后豁免」的那种绿:两处首轮红(Check Changeset 的 ADR-0087 处置、Check Links 的四条 ETL 死链)在上一轮就已修在源头。
第二次与 main 同步(#6528 / #6527 / #6516 / #6525 / #6517 / #6519 / #6515 / #6513 / #6511)
同一套 os-regen 四步。两处手工冲突,都不是生成物:
migrations/registry.ts—— 两侧各自在step17.semantic[]末尾追加,合并为四条:main 的driver-sql-distinct-bare-filter-typed加本 sweep 的三条。二选一会静悄悄丢掉一条 D3 记录。type-alias-convention.pin.test.ts—— 计数叙事三段合并。
ADR-0122 pin 计数:一次分支生命周期里被 rebase 了两次
751 分支点
754 #6037 / PR #6474 加三(ValidateDataIssue / Request / Response)
755 #5933 / PR #6515 加一(SpecifierValueDomain,新 z.enum,两形状重合)
748 本 sweep 减七
减数 7 是唯一稳定的操作数,被减数动了两次。另外记一笔同窗口的零贡献:#6527 退役了 AggregationFunction 的 array_agg / string_agg——那是枚举值收窄,def 还在,所以它对这个 pin 文件和四张 surface ratchet 都不可见(退役 playbook 的路线-读数对照表第一行)。同一天里「整 def 删除」与「枚举值收窄」两种形态并排出现,读数一个必须变、一个必须不变,拿错对照就会判错。这三点都写进了断言处的注释。
合并后实测 grep -c "^export type Iso" = 748,与断言一致。
同期条目存活自证(重生成后)
$ git diff origin/main -- packages/spec/json-schema.manifest | grep '^-' | grep -v '^---' | wc -l
23 # 恰为本 sweep 的 def 数
$ git diff origin/main -- packages/spec/json-schema.manifest | grep '^+' | grep -v '^+++' | wc -l
0 # 零新增
SpecifierValueDomain 在 api-surface/system.json 2 处命中;#6527 的 array_agg 退役文案在 data/query.zod.ts 完整存活。
合并树复验
pnpm --filter @objectstack/spec typecheck PASS
pnpm --filter @objectstack/spec test 341 files / 8731 tests PASS
check:authorable-surface / api-surface / docs / spec-changes / upgrade-guide /
strictness-ledger / generated / liveness / variant-docs 全绿
node scripts/check-adr-0087-registration.mjs PASS(3 条 declared-breaking 各带处置)
node scripts/check-nul-bytes.mjs 6127 个文件,零裸控制字节
标签
读回:documentation, size/xl, protocol:system, tests, tooling(均由 bot 打)。skip-changeset 不适用也未打——本 PR 带三份 .changeset/*.md,是有发布内容的改动。
git diff --stat origin/main...HEAD 现为 47 个文件(+1037 / −3238);比正文写的 43 多出的四个是第二轮 CI 修红带来的,逐个对应:content/docs/getting-started/quick-reference.mdx、packages/spec/src/integration/connector.zod.ts、content/docs/references/integration/connector.mdx(随前者重生成)、packages/spec/src/shared/alias-integrity.test.ts(一处指向已删文件的注释)。仍无 rider。
Generated by Claude Code
…8-i18n-label-contract-sweep Second conflict round on the pin count: the sweep's -7 (755->748) merged in under this branch's +1 (InlineLocaleMapSchema); receipts stacked in merge order, count recomputed from the file = 749. Generated files taken from origin/main verbatim; regenerated wholesale in the follow-up commit.
… tree Second regen round after #6526 merged: docs references, api-surface, authorable-surface, strictness-ledger counts. check:generated 10/10, check:spec-parsed-alias 749 OK, pin test 3/3.
`origin/main` 上有三个提交改了 schema 并各自重生成了 `content/docs/references/**`(#6512 i18n 标签契约、#6540 capability 注册、 #6526 ADR-0049 退役 sweep),与本分支的重生成在 13 个文件上相交。 `.gitattributes` 的 `merge=os-regen` 驱动按设计**没有做文本合并**,而是把这 13 个文件标记为「必须在合并后的树上重生成」—— 否则会落地 #6224 那种「零冲突 却陈旧」的组合。 本提交就是那次重生成:`gen:schema && gen:docs` 跑在合并后的树上,12 个文件 被修正,`check-regen-pending` 标记已清除。无一处手改 `.mdx`; `packages/spec/scripts/lib/format-type.ts` 与其测试**逐字未动**(main 上没有 任何提交碰过这两个文件)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AZgRyPVwi1jLb1mNNuUQ9o
Fixes #6486
Fixes #5295
Fixes #6239
Fixes #6414
ADR-0049 enforce-or-remove 退役 sweep。卡片 #6486 派了五个成员,逐单前置门跑完后落地三个,摘出两个——两个摘出成员的前提在
origin/main上被证伪(有活消费者),证据见文末「Dropped members」一节。摘出的两单不在 Fixes 行内,避免错误关单;其再分诊由 PM 办理。三个落地成员共用同一条路线:retirement playbook 的 route 3(整 def 删除)。三者都不是 authorable 键(没有任何一个是某个 schema 上的 key),全仓零
parse站点,因此既没有retiredKey()墓碑,也没有 D2 conversion——墓碑是写给「会写这个键的人」的处方,而这里没有人能写;conversion 是用来改写已有源文档的,而这里没有文档可改。声明落在RETIRED_DEFS_BY_MAJOR[17](23 个 def)加三条 D3SemanticMigration上,与 #4834 / #4938 / #4988 / #5055 同型。成员 1 — #5295
system/http-server.zod.ts的运行期词表声明位
packages/spec/src/system/http-server.zod.ts:201-336(退役前)原声明 四个 def、八个导出名:
ServerEventType/ServerEventSchema/ServerEventstartingstartedstoppingstoppedrequestresponseerror),带 ISO 时间戳与松散 payloadServerCapabilitiesSchema/ServerCapabilities/ServerCapabilitiesParsedwebsocketsseserverPushstreamingmiddlewarerouteParamscompression)加httpVersionsServerStatusSchema/ServerStatus墓碑现说 没有墓碑,按 route 3。文件里留下的退役注记逐行写明了每个形状对应的在役机制:生命周期是 transport 插件自己的 start/stop 缝;逐请求可观测性是
system/metrics.zod.ts+system/logging.zod.ts(以及OS_SERVER_TIMING);存活是/health;而「这个 server 能做什么」由它实现的 kernel 插件契约本身声明——它注册的那些缝就是能力声明,一份自报的能力记录只可能与之矛盾。零消费者测量证据(在
origin/main上重跑)这一单被 hold 了四天,理由具体而合理:响应/能力词表可能是给 host 实现者的参考面,「仓内零消费者」对这类形状的证据力弱于 authorable 键(CSS 变量式反驳)。所以解 hold 靠的是测量参考读者本身,而不是把同一条 grep 再跑一遍:
plugin-hono-server——本仓唯一的 host 实现——既不实现也不报告三者。其src/下没有任何 capability 记录、status 形状或事件联合;它注册的是路由与中间件。^(export )?(const|type|interface) 名字\b把全部声明定位在这一个文件;带引号精确名跨 objectstack / objectui 扫描,文件外零读者——幸存命中只有三条 ADR-0122 同构 pin(随 schema 一起删)与生成的参考页(它收录导出是因为导出存在,不是因为有人 import)。MiddlewareConfig就声明在这些形状上方十二行,解析到活消费者packages/runtime/src/middleware.ts:4,59。也就是说这轮扫描在这个文件里看得见读者——它只是没看见这三个形状的。system/ServerRateLimitConfig。它在同一份 manifest 里离system/ServerCapabilities只有四行、共享前缀,但它属于StackServerSecurity.rateLimit,是 #5006 带执行器一起收编的在役面。前缀相邻不是证据。成员 2 — #6239 View-Management 五方法
声明位
packages/spec/src/api/protocol.zod.ts:十套 schema 在:687-738,类型别名在:1359-1380,ViewProtocol接口在:1571-1577(均为退役前行号);另有packages/client/src/index.ts:5077-5081的五个响应类型再导出。原声明 一整套按
viewId寻址的 view CRUD:按 object 列举(带 list/form 过滤)、读一个、创建、局部更新、删除。十个 def、二十五个导出名。墓碑现说 没有墓碑,按 route 3。声明位留下的退役注记指向一直都在服务的两个面:view 的存储定义走通用 metadata 方法 +
type: 'view'(getMetaItem/getMetaItems/saveMetaItem/deleteMetaItem,路由/api/v1/meta/view/:name);解析后的渲染期 view 走getUiView(路由/api/v1/ui/view/:object/:type)。两者都不按viewId寻址——而按 id 寻址正是退役面唯一提供、且从没人实现的东西。零消费者测量证据(在
origin/main上重跑) 一个协议方法需要三样东西,这套面一样都没有:packages/metadata-protocol/src/protocol.ts里没有listViews/getView/createView/updateView/deleteView;该文件唯一的 view 解析方法是getUiView。packages/rest/src/rest-server.ts里viewId零命中——没有任何按 viewId 寻址的东西能经 HTTP 到达。ViewProtocol在本文件之外的唯一提及是content/docs/kernel/services-checklist.mdx,而它记的正是「声明了、没路由」。裸名 grep 捞到的相似命中都是别的契约:
packages/metadata/src/metadata-manager.ts的getView(name: string)是另一个类;objectuipackages/data-objectstack/src/index.ts的getView(objectName, viewId)内部走client.meta.getItem('view', …),即 metadata 路由。objectuipackages/types/src/data.ts的listViews/createView/updateView/deleteView是 objectui 自己 DataSource 接口的方法,签名按 objectName,不引用本仓任何 schema。为什么值得退而不是标注 代价已经量到了:#5948 的正文与其 2026-08-07 维护者裁决,都把
GetViewResponseSchema(本表,零实现)当成了GET /ui/view/:object/:type的契约,而那条路由声明的是GetUiViewResponseSchema——同一文件往上 250 行,差一个词。那次裁决的理由碰巧成立,这次退役就是不再靠这种运气。成员 3 — #6414 L2 ETL 层
声明位
packages/spec/src/automation/etl.zod.ts(整文件,841 行),packages/spec/src/automation/index.ts:15的 barrel 导出。原声明
ETLPipeline、ETLPipelineRun、ETLSource、ETLDestination、ETLTransformation,四个枚举(ETLEndpointType/ETLTransformationType/ETLSyncMode/ETLRunStatus)与ETL工厂。九个 def、二十七个导出名。墓碑现说 没有墓碑,按 route 3。
automation/index.ts与SYNC_ARCHITECTURE.md里的退役注记逐层给出去处,并如实留下一个空缺:连接器附带的同步是ConnectorSchema.syncConfig(在役、被解析、被执行);导入时的逐字段值转换是mapping.fieldMapping[].transform(REST 导入路径逐行执行,并在packages/spec/liveness/mapping.json里逐键记账);定时是system/job.zod.ts;而多源、多阶段、带 join / aggregate / 自定义 SQL 的搬运没有替代面——因为它从来也没有实现。它按 ADR-0049 的 enforce 路线回来:先有引擎,再有词表。零消费者测量证据(在
origin/main上重跑)packages/*/apps/*里非 spec 的引用只有两处 fumadocs 生成的文档源(apps/docs/.source/browser.ts/server.ts),不是执行器;objectui 零命中。packages/spec/liveness/下没有etl.json——ADR-0049 的门禁对这个面从来没有读数。这个「没有」之所以有意义而不是疏忽,靠的是同族对照:同一文件家族里被执行的那半边有台账(liveness/mapping.json),而shared/mapping.zod.ts的模块 TSDoc 明写transform是「applied row by row by the REST import path and recorded live, key by key」。migrations/registry.ts的etl字符串链路已展开核实:它不是 loader 路径,而是ETLPipeline.retryis a third retry-policy vocabulary that #4661 的收敛没有覆盖到 #4962 那条etl-retry-converged-onto-retry-policy语义迁移条目的 id。本 PR 按 playbook §0 同 major 规则吸收了它(见下)。packages/spec/docs/SYNC_ARCHITECTURE.md此前(a)在 L34-40 把ETLPipeline指定为 L1 退役后作者的推荐去处,(b)在 L158-171 逐行宣传十种 transformation,具体到script | Custom JavaScript/Python | return row.price * 1.1这种能照抄的程度。退役 schema 而不改文档就是自相矛盾;更要紧的是,把 L1 的作者转介到第二个没有执行器的层,是缺陷在扩散而不是在收敛。文档现在改成单层架构 + 两节退役记录,决策矩阵里原本指向 L2 的两行如实答「没有这个面,原因如下」,迁移指南的方向也反了过来(「从 L2 迁到现存的东西」)。吸收 #4962 的
etl-retry-converged-onto-retry-policy语义条目按 playbook §0 被本次退役吸收:两者都落在未发布的 protocol 17,复合之后,「在一个不跨过这个 major 的形状上改名retry.maxAttempts→maxRetries」没有可观测效果;保留两条会让升级者去改一个同一次升级就删掉的键,并破坏 fixture 互斥契约。maxAttempts的retiredKey()墓碑随承载它的形状一起走——这比墓碑本身更强,因为已经没有retry块可以把这个键写进去。Dropped members(前提证伪,不在 Fixes 行内)
卡片纪律是「任一成员出现活消费者 ⇒ 摘出并在报告说明」。两单命中。
#5703
ActionDescriptor.supportsPause/isAsync——supportsPause有活读者issue 记的「三仓零非测试读者」在
origin/main上已不成立。卡片本身提到了engine.ts的注册告警,但处理面比卡片预估的大得多:packages/services/service-automation/src/engine.ts:1389——if (descriptor.supportsPause !== true) return;,warnIfResumeAuthorityUndeclared的判词入口(automation:resumeAuthoritydefaults to'any', so every future pausing node ships fail-open — ADR-0044 says this is "tracked separately" and nothing tracks it #5561 / PR feat(spec,service-automation,devx): 未声明 resumeAuthority 的 pausing 节点类型不再静默 fail-open (#5561) #5725,两天前落地的安全告警)。scripts/check-resume-authority-declared.mjs(430 行,用 TypeScript 编译器 API 扫描supportsPause: true声明并要求resumeAuthority),挂在.github/workflows/lint.yml:436,package.json里是check:resume-authority-declared。packages/services/service-automation/src/resume-authority-declaration.test.ts(19 处引用)、resume-authority-gate.test.ts。packages/spec/src/automation/node-executor.zod.ts:293,370的 TSDoc 明确把该门禁写进契约说明。退役这个字段等于删掉维护者两天前刚落的告警面、一条 CI 门禁与一个测试文件——超出「spec + 该告警行的机械随动」,按卡片指示报告而不硬做。
isAsync单独看确实仍是零读者(五个执行器里的isAsync: true都是写方),但它是该单两字段对的另一半;只退一半会改变这单的叙事,交 PM 重新分诊。#6247
qa/testing.zod.ts—— 有完整执行链,含在售 CLI 命令这是五个成员里唯一「维护者已裁 retire」的一个,而它的前提被证伪:issue 的 grep 只覆盖了
*Schema标识符,漏掉了类型名消费者。实际链路:packages/core/src/qa/runner.ts——TestRunner.runSuite/runScenario/runStep,逐字段读suite.scenarios、scenario.setup|steps|teardown|id、step.name|action|capture|assertions。packages/core/src/qa/http-adapter.ts——HttpTestAdapter.execute对action.type做 switch,分支正是TestActionTypeSchema的取值(create_record/update_record/delete_record/read_record/query_records/api_call/wait),并读action.target/action.payload/action.user。packages/core/src/qa/adapter.ts——TestExecutionAdapter接口。packages/core/src/index.ts:25——export * as QA from './qa/index.js',已发布。packages/cli/src/commands/test.ts—— 在售的 oclif 命令os test,文档见content/docs/deployment/cli.mdx:987,1012-1020与packages/cli/README.md:104。也就是说这个域不是「声明了没人接」:它有执行器(
TestRunner)也有入口(os test),只是 schema 没有parse站点、类型才是运行器读的契约。退役它等于删掉一条在售 CLI 命令底下的类型契约,远超 spec 退役范围。证据交维护者按新读数重裁。Scope 自证
git diff --stat origin/main...HEAD= 43 个文件,逐行对应上面的清单,无 rider:system/http-server.zod.ts幸存导出ServerEvent*/ServerCapabilities/ServerStatus全仓零消费者 —— 响应/能力形状,ADR-0049 判定待做 #5295):packages/spec/src/system/http-server.zod.ts、http-server.test.tspackages/spec/src/api/protocol.zod.ts、protocol.test.ts、packages/client/src/index.ts、content/docs/kernel/services-checklist.mdxpackages/spec/src/automation/etl.zod.ts+etl.test.ts+etl-author-shape.test.ts(删除)、automation/index.ts、packages/spec/docs/SYNC_ARCHITECTURE.md、packages/spec/PROTOCOL_MAP.md、packages/spec/scripts/build-docs.ts、src/shared/retry-policy.zod.ts+retry-policy.test.ts、src/automation/sync-retirement.test.ts、src/integration/connector-author-shape.test.ts、docs/audits/2026-07-unknown-key-strictness-ledger.mdpackages/spec/src/migrations/registry.ts(RETIRED_DEFS_BY_MAJOR+ 三条 D3)、src/type-alias-convention.pin.test.tsmajor([spec] View-Management 五方法(listViews/getView/createView/updateView/deleteView)是零实现零路由的声明面 —— 已导致 #5948 把它的响应 schema 误当成线上路由的契约 #6239 那份同时标@objectstack/client: major,因为它的公开再导出面缩小了)api-surface/(3)、authorable-surface/(3)、authorable-defaults/(2)、json-schema.manifest/(3)、spec-changes.json、docs/protocol-upgrade-guide.md、content/docs/references/**(5)、strictness-ledger.counts.md生成物读数(按 playbook 的路线-读数对照)
三个成员都是整 def 删除,所以四张 ratchet 必须变化,而且确实变了——如果它们纹丝不动,那才说明 def 根本没真删掉:
gen:schema的两道门按顺序开火并被逐条满足:先是 manifest 删除门点名 23 个 def 要求在RETIRED_DEFS_BY_MAJOR里申报,然后 per-key 门把 75 条 baseline 行判为 #4650 路径 3(def no longer emitted by this build)。ADR-0122 pin 计数 751 → 744(#5295 三行、#6239 四行、#6414 零行——ETL 的别名全都已有Parsed对子),收据按文件惯例写在计数断言处。反向验证(方向先判后跑)
三个成员的 pin 都是朴素的红方向:新增断言是
false-expecting 的存在性检查(name in module),把任一被删的肢体贴回去,正好是这些断言变红。不是那两种反转方向——因为下游没有任何东西在数这些名字(与 PR #5046 的 count 情形不同)。这一判断写在测试注释里,而不是事后补的。Fixture 三分法
http-server.test.ts的四个 describe(事件枚举 / 事件 parse / 能力默认值 / 状态机)与protocol.test.ts的validates Views operations—— 它们断言的就是被删形状自身的行为,重新指向别的 schema 只会留下「因为什么都没产出所以还绿」的空壳。retry-policy.test.ts的maxAttempts墓碑用例。墓碑随承载它的形状走了;把它改写到flow上会与上一行断言重复,却看起来像保住了覆盖。sync-retirement.test.ts里那条toContain('ETLPipelineSchema')同理——它当年是用来证明「L1 退役止于 L1」的,现在 L2 也退了,留着就是在断言一个本仓刻意删掉的层还活着;换成了 15 个名字的not.toContain。connector-author-shape.test.ts。它的兄弟门automation/etl-author-shape.test.ts随 L2 一起删除,而那个兄弟门持有SYNC_ARCHITECTURE.md的 ```typescript 总块数 pin——「加一个代码块必须是有意为之」的唯一保障。门被删会静悄悄带走它的断言,这正是文档门唯一会无声失效的方式,所以总数 pin 迁到了这里(现值 2),连同「L2 的 Before 片段刻意用纯文本围栏、不是typescript」的显式 pin。验证
check:i18n/check:i18n-coverage在本 worktree 报「PREREQUISITE NOT MET —— CLI 未构建」(环境前提,非本改动)。已单独核实本改动不动 i18n 面:退役的形状没有任何*.form.ts输入,packages/platform-objects/src/apps/translations/*.generated.ts里也没有对应条目。🤖 Generated with Claude Code
https://claude.ai/code/session_011M7UwH25Unfi73UHim7ajY
Generated by Claude Code