Skip to content

Commit a1a3771

Browse files
committed
feat(spec)!: 双源 C4 收敛 — Session 归 ./api,./identity 侧死删 (#4641)
`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
1 parent 0a936ea commit a1a3771

12 files changed

Lines changed: 122 additions & 221 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
"@objectstack/spec": major
3+
---
4+
5+
Resolve the `Session` dual source — `./api` keeps the bare names, the `./identity` declaration is removed (#4641)
6+
7+
`Session` and `SessionSchema` were each declared **twice**, once on
8+
`@objectstack/spec/api` and once on `@objectstack/spec/identity`. Which shape a
9+
consumer got depended only on which entry point they imported from — the #4411
10+
trap — and the two did not even agree on field names, so the mistake surfaced as
11+
a runtime `undefined`, not a type error.
12+
13+
**FROM → TO**
14+
15+
| Import | Before | After |
16+
|:--|:--|:--|
17+
| `@objectstack/spec/api` | `Session` / `SessionSchema` | unchanged — this is now the only declaration |
18+
| `@objectstack/spec/identity` | `Session` / `SessionSchema` (a second, different shape) | **removed** |
19+
20+
The surviving `./api` shape is the wire contract:
21+
22+
```ts
23+
{ id: string; expiresAt: string; token?: string; ipAddress?: string; userAgent?: string; userId: string }
24+
```
25+
26+
It is embedded in `SessionResponseSchema`, the body served for
27+
`AuthEndpointPaths.getSession` (`/get-session`, `/me`, `/refresh`).
28+
29+
The removed `./identity` shape was
30+
`{ id, sessionToken, userId, activeOrganizationId?, expires, createdAt, updatedAt, ipAddress?, userAgent?, fingerprint? }`.
31+
32+
**Nothing consumes it.** An import-statement-level scan across framework, `cloud`
33+
and `objectui` found no importer outside its own unit test, and it was wired into
34+
no parent schema. It had also drifted from the record it claimed to describe: the
35+
**enforced** session row is the `sys_session` object in
36+
`@objectstack/platform-objects`, which spells the columns `token` and
37+
`expires_at` (matching `./api`, not `./identity`) and has no `fingerprint` at all.
38+
39+
**If you were importing `Session` from `@objectstack/spec/identity`**, change the
40+
specifier to `@objectstack/spec/api` and rename the fields you read:
41+
`sessionToken``token`, `expires``expiresAt`. `createdAt` / `updatedAt` /
42+
`activeOrganizationId` / `fingerprint` are not on the wire shape — read the
43+
persisted record through the `sys_session` object, which is what the migration
44+
and the auth plugin actually enforce.
45+
46+
Reference docs follow the declaration: `Session` is now documented on the
47+
`references/api/auth` page (the module that declares it) instead of the
48+
name-collision page `references/api/identity`, which is removed.

content/docs/references/api/auth.mdx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ and Access Control.
1818
## TypeScript Usage
1919

2020
```typescript
21-
import { AuthProvider, LoginRequestSchema, LoginType, RefreshTokenRequestSchema, RegisterRequestSchema, SessionResponseSchema, SessionUserSchema, UserProfileResponseSchema } from '@objectstack/spec/api';
22-
import type { AuthProvider, LoginRequest, LoginType, RefreshTokenRequest, RegisterRequest, SessionResponse, SessionUser, UserProfileResponse } from '@objectstack/spec/api';
21+
import { AuthProvider, LoginRequestSchema, LoginType, RefreshTokenRequestSchema, RegisterRequestSchema, SessionSchema, SessionResponseSchema, SessionUserSchema, UserProfileResponseSchema } from '@objectstack/spec/api';
22+
import type { AuthProvider, LoginRequest, LoginType, RefreshTokenRequest, RegisterRequest, Session, SessionResponse, SessionUser, UserProfileResponse } from '@objectstack/spec/api';
2323

2424
// Validate data
2525
const result = AuthProvider.parse(data);
@@ -93,6 +93,22 @@ const result = AuthProvider.parse(data);
9393
| **image** | `string` | optional | |
9494

9595

96+
---
97+
98+
## Session
99+
100+
### Properties
101+
102+
| Property | Type | Required | Description |
103+
| :--- | :--- | :--- | :--- |
104+
| **id** | `string` || |
105+
| **expiresAt** | `string` || |
106+
| **token** | `string` | optional | |
107+
| **ipAddress** | `string` | optional | |
108+
| **userAgent** | `string` | optional | |
109+
| **userId** | `string` || |
110+
111+
96112
---
97113

98114
## SessionResponse

content/docs/references/api/identity.mdx

Lines changed: 0 additions & 35 deletions
This file was deleted.

content/docs/references/api/meta.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
"core-services",
3131
"events",
3232
"export",
33-
"identity",
3433
"metadata",
3534
"package-api",
3635
"package-registry",

content/docs/references/identity/identity.mdx

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ defines "how to login".
2222
## TypeScript Usage
2323

2424
```typescript
25-
import { AccountSchema, ApiKeySchema, SessionSchema, UserSchema, VerificationTokenSchema } from '@objectstack/spec/identity';
26-
import type { Account, ApiKey, Session, User, VerificationToken } from '@objectstack/spec/identity';
25+
import { AccountSchema, ApiKeySchema, UserSchema, VerificationTokenSchema } from '@objectstack/spec/identity';
26+
import type { Account, ApiKey, User, VerificationToken } from '@objectstack/spec/identity';
2727

2828
// Validate data
2929
const result = AccountSchema.parse(data);
@@ -82,26 +82,6 @@ const result = AccountSchema.parse(data);
8282
| **metadata** | `Record<string, any>` | optional | Custom metadata |
8383

8484

85-
---
86-
87-
## Session
88-
89-
### Properties
90-
91-
| Property | Type | Required | Description |
92-
| :--- | :--- | :--- | :--- |
93-
| **id** | `string` || Unique session identifier |
94-
| **sessionToken** | `string` || Session token |
95-
| **userId** | `string` || Associated user ID |
96-
| **activeOrganizationId** | `string` | optional | Active organization ID for context switching |
97-
| **expires** | `string` || Session expiry timestamp |
98-
| **createdAt** | `string` || Session creation timestamp |
99-
| **updatedAt** | `string` || Last update timestamp |
100-
| **ipAddress** | `string` | optional | IP address |
101-
| **userAgent** | `string` | optional | User agent string |
102-
| **fingerprint** | `string` | optional | Device fingerprint |
103-
104-
10585
---
10686

10787
## User

docs/audits/2026-07-unknown-key-strictness-ledger.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ it rather than rediscover it.
544544
| `cloud/` | 83 | wire | multi-tenant runtime |
545545
| `ai/` | 75 | mixed | agent/tool/skill definitions authored (partially strict already); model/provider payloads wire |
546546
| `integration/` | 64 | wire | connector payloads — upstream adds fields freely |
547-
| `identity/` | 34 | mixed | position/user shapes authored (`PositionSchema` **strict as of #4001 step 2**, with the ADR-0010 envelope declared); auth payloads wire |
547+
| `identity/` | 33 | mixed | position/user shapes authored (`PositionSchema` **strict as of #4001 step 2**, with the ADR-0010 envelope declared); auth payloads wire. **34 → 33 in #4641**: `identity.zod.ts` lost its `SessionSchema` site — a second, importerless declaration of a name `api/auth.zod.ts` already owned (the #4411 dual-source trap), deleted rather than reclassified |
548548
| `shared/` | 25 | n/a | utilities and building blocks; strictness decided at the consuming schema |
549549
| `qa/` | 6 | n/a | test fixtures |
550550

packages/spec/api-surface.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4412,8 +4412,6 @@
44124412
"SCIMUser (type)",
44134413
"SCIMUserSchema (const)",
44144414
"SCIM_SCHEMAS (const)",
4415-
"Session (type)",
4416-
"SessionSchema (const)",
44174415
"TokenPayload (interface)",
44184416
"User (type)",
44194417
"UserSchema (const)",

packages/spec/authorable-surface.json

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4095,16 +4095,6 @@
40954095
"identity/SCIMUser:userName",
40964096
"identity/SCIMUser:userType",
40974097
"identity/SCIMUser:x509Certificates",
4098-
"identity/Session:activeOrganizationId",
4099-
"identity/Session:createdAt",
4100-
"identity/Session:expires",
4101-
"identity/Session:fingerprint",
4102-
"identity/Session:id",
4103-
"identity/Session:ipAddress",
4104-
"identity/Session:sessionToken",
4105-
"identity/Session:updatedAt",
4106-
"identity/Session:userAgent",
4107-
"identity/Session:userId",
41084098
"identity/User:createdAt",
41094099
"identity/User:email",
41104100
"identity/User:emailVerified",

packages/spec/dual-source-exports.baseline.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121
"RateLimitConfigSchema — [./integration (const)] ≠ [./shared (const)]",
2222
"RetryPolicy — [./automation (type)] ≠ [./system (type)]",
2323
"RetryPolicySchema — [./automation (const)] ≠ [./system (const)]",
24-
"Session — [./api (type)] ≠ [./identity (type)]",
25-
"SessionSchema — [./api (const)] ≠ [./identity (const)]",
2624
"TenantPlan — [./cloud (type)] ≠ [./system (type)]",
2725
"TenantPlanSchema — [./cloud (const)] ≠ [./system (const)]"
2826
]

packages/spec/json-schema.manifest.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,6 @@
864864
"identity/SCIMPatchRequest",
865865
"identity/SCIMPhoneNumber",
866866
"identity/SCIMUser",
867-
"identity/Session",
868867
"identity/User",
869868
"identity/VerificationToken",
870869
"integration/CircuitBreakerConfig",

0 commit comments

Comments
 (0)