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(第二个类型声明符号)
export type HttpRequest = z.infer< typeof HttpRequestSchema >;

// TO —— re-export ./shared 的唯一声明,挪到文件顶部 schema re-export 旁边
export type { 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 对象,所以这不是推断,是编译器验过的(临时脚本,已删除):

type Equal< X, Y > = (< T >() => T extends X ? 1 : 2) extends (< T >() => T extends Y ? 1 : 2) ? true : false;
type Assert< T extends true > = T;

type PreFixUiHttpRequest = z.infer< typeof HttpRequestSchema >;   // 逐字复刻旧的那一行

export type _A = Assert< Equal< PreFixUiHttpRequest, SharedHttpRequest > >;
export type _B = Assert< Equal< PreFixUiHttpRequest, UiHttpRequest > >;
export type _C = Assert< Equal< UiHttpRequest, SharedHttpRequest > >;
export type _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 重新声明第二份 HttpRequestSchema AssertionError: 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

claude added 2 commits August 2, 2026 17:43
`@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

vercel Bot commented Aug 2, 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 2, 2026 5:48pm

Request Review

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

github-actions Bot commented Aug 2, 2026

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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants