Skip to content

feat(spec)!: 双源 C4 收敛 — Session 归 ./api,./identity 侧死删 (#4641) - #4643

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-4641-session-dual-source
Aug 2, 2026
Merged

feat(spec)!: 双源 C4 收敛 — Session 归 ./api,./identity 侧死删 (#4641)#4643
os-zhuang merged 1 commit into
mainfrom
claude/issue-4641-session-dual-source

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #4641

#4535 C 组第四簇(C4)。Session / SessionSchema 各有两处声明,消费者拿到哪个形状只取决于 import 路径(#4411 陷阱)—— 而两侧连字段名都不一致,所以写错的表现是运行时 undefined,不是类型错误。

判真源:三仓 import 语句级扫描

形状 消费方 / runtime 读取点
./api(api/auth.zod.ts) { id, expiresAt, token?, ipAddress?, userAgent?, userId } —— 接进 SessionResponseSchema,即 AuthEndpointPaths.getSession(/get-session/me/refresh)的响应体
./identity(identity/identity.zod.ts) { id, sessionToken, userId, activeOrganizationId?, expires, createdAt, updatedAt, ipAddress?, userAgent?, fingerprint? } 零消费方 —— framework / cloud / objectui 三仓除自身单测外无任何 importer,未接进任何父 schema

判定 ./identity 侧为死侧的决定性证据不只是「没人 import」,还有它已经偏离了自己声称描述的那张表:被强制执行的会话记录是 packages/platform-objects/src/identity/sys-session.object.tssys_session 对象,列名是 token / expires_at(与 ./api 一致,而非 ./identitysessionToken / expires),且根本没有 fingerprint。cloud 侧确实读 activeOrganizationId,但走的是 better-auth 自己的类型(active-org-session-hook.ts 只 import 了 @objectstack/spec/contracts),不经 spec 的 Session

也就是说 ./identitySession 是一份没人执行、且已经漂移的 better-auth 词汇复述 —— 正是 ADR-0049「declared but unenforced」要清掉的东西。

处置

路线一:死删无消费方一侧(v17 major 窗口)。./identitySessionSchema / Session 移除,./api 成为裸名唯一所有者。未做收敛+re-export:两者是「存储行」与「线上投影」两个概念,收敛要么把 sessionToken / fingerprint 泄进 REST 响应体,要么收窄记录 —— 都不对。也未改名:改名会把一个零消费方的死声明换个名字留下来。

dual-source-exports.baseline.json 恰好删掉 issue 指名的 2 行,24 → 22,只减不增:

- Session — [./api (type)] ≠ [./identity (type)]
- SessionSchema — [./api (const)] ≠ [./identity (const)]

回归 pin —— 顺带发现手册推荐的 pin 是空转的

手册(C1 #4581 / C3 #4638 先例)推荐用编译期条件类型做 pin。落之前我实测了一下,它在 packages/spec 里不可能失败:

  1. packages/spec/tsconfig.jsonexclude**/*.test.ts —— pnpm typecheck 根本不编译测试文件;
  2. vitest.config.ts 没开 typecheck.enabled,vitest 走 esbuild 剥类型,不做类型检查。

sabotage 验证:把 export const SessionSchema 加回 identity.zod.ts 再跑 tsc --noEmit,pin 零报错

附带第二个坑:keyof typeof import(...) 只枚举 value 导出,type-only 导出对它不可见,所以对裸类型名写这类断言即便被编译也是空转(已用最小复现验证)。

所以本 PR 没有跟着写一个「看起来像门禁、实际不会红」的 pin,改用运行时模块命名空间断言,并在注释里写明取舍;裸类型交给 check:dual-source-exports(读构建产物 .d.ts,type 和 const 都枚举)。同样的 sabotage 下新断言立刻红:

Tests  1 failed | 19 passed (20)

范围外发现已另立 #4642(unassigned)记录,影响 #4581 / #4638 已落地的 pin —— 本 PR 不修。

连带产物

changeset

.changeset/session-dual-source-c4.md —— @objectstack/spec major,含 FROM → TO 表与迁移指引(sessionTokentoken,expiresexpiresAt;createdAt / updatedAt / activeOrganizationId / fingerprint 不在线上形状,改读 sys_session 对象)。

验证(全绿)

packages/spec 下,全部前台跑、共享 flock 串行、--max-old-space-size=4096:

门禁 结果
pnpm --filter @objectstack/spec build
pnpm check:dual-source-exports 4313 names across 16 entry points — 160 re-exported, 22 accepted dual-source (baseline)
pnpm check:generated All 8 generated artifacts are up to date.
pnpm test 291 passed (291) / 7266 passed (7266)
check:liveness ✅ 全部 governed-type 属性已分类
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 1884 types + 1638 schemas,无 any
check:skill-examples 202 prose examples type-check
全仓 pnpm typecheck 122 successful, 122 total

收尾前 git fetch origin main && git merge origin/mainAlready up to date(main 仍在 0a936ea62),无需重跑。

🤖 Generated with Claude Code

https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL


Generated by Claude Code

`Session` / `SessionSchema` 各有两处声明,一处在 `api/auth.zod.ts`,一处在
`identity/identity.zod.ts`。消费者拿到哪个形状只取决于 import 路径(#4411
陷阱),而两者连字段名都不一致 —— 写错的表现是运行时 `undefined`,不是类型
错误。

三仓(framework / cloud / objectui)import 语句级扫描:

- `./api` 侧是活的:形状 `{ id, expiresAt, token?, ipAddress?, userAgent?,
  userId }`,被接进 `SessionResponseSchema` —— `AuthEndpointPaths.getSession`
  (`/get-session`、`/me`、`/refresh`)的响应体,是真正的 runtime 读取点。
- `./identity` 侧零消费方:形状 `{ id, sessionToken, userId,
  activeOrganizationId?, expires, createdAt, updatedAt, ipAddress?, userAgent?,
  fingerprint? }`,除自身单测外无任何 importer,未接进任何父 schema。它还偏离
  了自己声称描述的那张表 —— **被强制执行**的会话记录是 platform-objects 的
  `sys_session` 对象,列名是 `token` / `expires_at`(与 `./api` 一致,而非
  `./identity`),且根本没有 `fingerprint`。cloud 侧读 `activeOrganizationId`
  走 better-auth 自己的类型,不经 spec。

处置(路线一,死删无消费方一侧,v17 major 窗口):`./identity` 的
`SessionSchema` 与 `Session` 移除,`./api` 成为裸名唯一所有者。
dual-source-exports.baseline.json 恰好删掉指名的 2 行(24 -> 22)。

回归 pin 用**运行时**断言而非 C1/C3 的编译期条件类型 —— 后者在这里是空转:
`packages/spec/tsconfig.json` 排除了 `**/*.test.ts`,vitest 也不做类型检查,
所以那类 pin 不可能失败(已另立 #4642 记录,影响 #4581/#4638 已落地的 pin)。
本 PR 的断言经过 sabotage 验证:把声明加回去,测试立刻红。

连带更新:json-schema.manifest 去掉 identity/Session;authorable-surface 去掉
该 schema 的 10 个 key(整形状移除,同 #4638 先例);api-surface 重新生成。
reference docs 跟着声明走 —— `Session` 现在文档化在 `references/api/auth`
(真正声明它的模块)上,名字碰撞产生的 `references/api/identity` 页随之消失。
严格性台账 `identity/` 粗粒度行 34 -> 33 并写明掉站点的原因。
docs-import-surface 基线未触发。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
@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 1:36pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling 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 size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec 双源清账 C4:Session / SessionSchema(./api ≠ ./identity)—— 2 条

2 participants