Skip to content

refactor(spec): 双源 C11 收敛 — HttpRequest 类型别名改为 re-export ./shared 的唯一声明 (#4688) - #4689

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-4688-http-request-dual-source
Aug 2, 2026
Merged

refactor(spec): 双源 C11 收敛 — HttpRequest 类型别名改为 re-export ./shared 的唯一声明 (#4688)#4689
os-zhuang merged 2 commits into
mainfrom
claude/issue-4688-http-request-dual-source

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#4688

#4535 的 C11 簇,v17 收口三簇之二。基线 19 → 18,只减不增。

改了什么

HttpRequest 这个名字过去在 @objectstack/spec/shared@objectstack/spec/ui 解析到两份不同的类型声明(#4411 陷阱)。现在 ./ui re-export ./shared 的那一份。

// FROM —— ui/view.zod.ts 底部的本地 infer(第二个类型声明符号)exporttypeHttpRequest=z.infer<typeofHttpRequestSchema>;// TO —— re-export ./shared 的唯一声明,挪到文件顶部 schema re-export 旁边exporttype{HttpRequest}from'../shared/http.zod';

派发单的定位表复核无误:HttpRequestSchema从来只有一份声明(shared/http.zod.ts:46),ui/view.zod.ts:21 import 进来、:37 原样 re-export,所以基线里根本没有 HttpRequestSchema 行。唯一的双源点就是那个本地类型别名。处置后它变成基线表头明说不计的 "a re-export of one declaration from many entries"。

构建产物可证收敛成功 —— 两个入口现在指向同一个 chunk 的同一个符号:

dist/ui/index.d.ts: export { …, d as HttpRequest, … } from '../http.zod-BLsW0NuY.js';
dist/shared/index.d.ts: export { …, d as HttpRequest, … } from '../http.zod-BLsW0NuY.js';

⚠️ 级别是 patch,不是 major —— 已实证零消费者影响

派发单要求「自己判,不要为了凑 major 而声称破坏」。结论:FROM 与 TO 是同一个类型,升级者无需做任何事。

两个别名 z.infer 的是同一个 schema 对象,所以这不是推断,是编译器验过的(临时脚本,已删除):

typeEqual<X,Y>=(<T>()=>TextendsX ? 1 : 2)extends(<T>()=>TextendsY ? 1 : 2) ? true : false;typeAssert<Textendstrue>=T;typePreFixUiHttpRequest=z.infer<typeofHttpRequestSchema>;// 逐字复刻旧的那一行exporttype_A=Assert<Equal<PreFixUiHttpRequest,SharedHttpRequest>>;exporttype_B=Assert<Equal<PreFixUiHttpRequest,UiHttpRequest>>;exporttype_C=Assert<Equal<UiHttpRequest,SharedHttpRequest>>;exporttype_NEGATIVE=Assert<Equal<UiHttpRequest,{totallyDifferent: true}>>;// 负对照

tsc --ignoreConfig --noEmit --strict 输出 —— 只有负对照报错,证明前三条不是空转:

tmp-from-to-identity.ts(25,32): error TS2344: Type 'false' does not satisfy the constraint 'true'.

配套证据(重新 build 后):api-surface.jsonauthorable-surface.jsonspec-changes.json全部零改动。零可作者化 key 消失、零 tombstone、零 ADR-0087 conversion。

关于 #4684(C9)的门禁死结:确认未被惊动。本簇不改任何 def、不动 HttpRequestSchema 声明,check:authorable-surface 与 ratchet 全程无感,authorable-surface.json 未被手编(#4650 纪律)。

回归 pin(三条,全部 sabotage 验证)

src/ui/view.test.ts 新增三条运行时断言。#4642 已证本包编译期 pin 空转(tsconfig.json 排除 **/*.test.ts、vitest 不开 typecheck)—— 这条同样适用于该文件已有的type HttpRequest 具名导入:它被 vitest transform 抹掉,从来就没有保障过这个导出还在。第三条断言正是来补这个洞的:用 TypeScript compiler API 在 src/ 上做符号身份解析(check:dual-source-exports 在 dist 上做的同一件事),因为 HttpRequest类型,运行时看不见,而它恰恰是本簇唯一改动的东西。

sabotage实际输出
还原旧的本地 infer 别名AssertionError: expected 'src/ui/view.zod.ts:2056' to be 'src/shared/http.zod.ts:54'
删掉 re-export 不补(派发单点名的现实风险)AssertionError: ./ui must still export the name `HttpRequest`: expected undefined to be truthy
./ui 重新声明第二份 HttpRequestSchemaAssertionError: expected [Function] to be [Function] // Object.is equality

第二条同时正是验收项「ui/view.test.ts 的既有具名类型导入继续可用」的守卫。

验证

已合并 origin/main(含刚落地的 #4685),合并后重新 build 并重跑生成物门禁。

check:dual-source-exports ✅ 4323 names across 16 entry points — 166 re-exported, 18 accepted dual-source (baseline) ← 19 → 18
check:generated ✓ All 8 generated artifacts are up to date
check:liveness ✓ all governed-type properties are classified …
check:strictness-ledger ✓ 67 file(s) across 5 triaged director(ies)
check:empty-state ✓ all classified (1 closed, 2 open, 4 output, 9 scope)
check:variant-docs ✓ 20 discriminated union(s) — 8 governed, 12 exempt
check:exported-any ✅ 1893 types + 1639 schemas across 16 entry points
check:skill-examples ✅ 202 prose examples type-check against @objectstack/spec
pnpm --filter @objectstack/spec test Test Files 292 passed (292) / Tests 7333 passed (7333)
pnpm typecheck (全仓) Tasks: 122 successful, 122 total

pnpm typecheck 全仓那次跑在合并前;合并后按手册第 10 条按重叠面收窄,重跑了 spec 的 build / check:generated / typecheck / test。

⛔ 未纳入:紧挨着的 HttpMethod(留给维护者决定)

ui/view.zod.ts 下一行 export type HttpMethod = z.infer< typeof HttpMethodSchema > 我已确认是完全相同的形状 —— 同样只有 type 双源、同样从 import 进来的同一个 HttpMethodSchema infer、同样零 authorable key。基线行 HttpMethod — [./api, ./shared (type)] ≠ [./ui (type)] 仍在,一行改动即可清掉。

但范围是维护者定的,#4535 已把它排进 v18,故本 PR 不动。若要并进来请直接说,改动约等于零。

一个差别值得记一笔:HttpMethod 比本簇多一层复杂度 —— shared/http.zod.tsHttpMethod(7 值,:20)和 HttpMethodSchema(5 值,:37)是两个不同的枚举,而 ./uiHttpMethod 类型 infer 自 5 值的那个。所以它不像本簇那样必然零类型差异,并进来时需要单独核 FROM → TO。

🤖 Generated with Claude Code

https://claude.ai/code/session_0176qgxgCXTJCUv4YFLtusP9


Generated by Claude Code

`@objectstack/spec/shared` and `@objectstack/spec/ui` both exported the name
`HttpRequest` for DIFFERENT type declarations — a row on
`dual-source-exports.baseline.json`, judged by symbol identity (#4411 trap).
This cluster is the degenerate case of that trap. `HttpRequestSchema` was never
duplicated: `ui/view.zod.ts` imports it from `shared/http.zod.ts` and re-exports
it verbatim, which is why the baseline carries no `HttpRequestSchema` row. The
only split was the local type alias at the bottom of view.zod.ts —
`z.infer<typeof HttpRequestSchema>` over the very same schema object, so a
second declaration symbol carrying an identical shape. It is now a re-export of
shared's declaration, which the baseline header explicitly does not count.
Baseline: 19 -> 18.
NOT breaking, and deliberately not labelled so. #4535 calls the three v17
clusters breaking wholesale; this one is verified otherwise. FROM and TO infer
from one schema object, and the compiler agrees: `Equal<PreFixUiHttpRequest,
UiHttpRequest>` and `Equal<PreFixUiHttpRequest, SharedHttpRequest>` both hold,
with a deliberately-false negative control erroring TS2344 to prove the pair is
not vacuous. api-surface.json, authorable-surface.json and spec-changes.json are
all byte-identical after a rebuild — zero authorable key movement, zero
tombstone, zero conversion. Overstating breakage pollutes the upgrade guide as
surely as understating it, so the changeset is patch.
Regression pin: three RUNTIME assertions in ui/view.test.ts. #4642 established
that a compile-time pin in this package is dead text (tsconfig excludes
**/*.test.ts, vitest never type-checks), and that applies to the pre-existing
`type HttpRequest` import in that file too — it is erased, so it never
guaranteed the export survives. The third assertion closes that hole by
resolving symbol identity through the TypeScript API over src/, the same
measurement check:dual-source-exports makes over dist. All three sabotage-verified.
`HttpMethod` on the next line is the identical shape and stays — #4535 scheduled
it for v18, and scope is the maintainer's to set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176qgxgCXTJCUv4YFLtusP9
@vercel

vercelBot commented Aug 2, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 2, 2026 5:48pm

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests protocol:ui tooling size/m labels Aug 2, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

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

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/spec)
  • content/docs/automation/approvals.mdx(via @objectstack/spec)
  • content/docs/automation/connectors.mdx(via @objectstack/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via packages/spec)
  • content/docs/concepts/north-star.mdx(via packages/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/cli.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via packages/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via packages/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/spec)
  • content/docs/kernel/services.mdx(via @objectstack/spec)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/rls.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx(via @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v16.mdx(via @objectstack/spec)
  • content/docs/releases/v17.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)
  • content/docs/ui/actions.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)
  • content/docs/ui/translations.mdx(via @objectstack/spec)
  • content/docs/ui/views.mdx(via @objectstack/spec)

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 documentationprotocol:uisize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec 双源清账 C11:HttpRequest(./shared ≠ ./ui)—— 1 条

2 participants

@os-zhuang@claude