feat(spec)!: 双源 C1 收敛 — WebhookConfig / WebhookEvent 归 ./integration,./api 侧死删 + 改名 OpenApiWebhookEvent (#4572) - #4581
Merged
Conversation
…pi pair removed/renamed, ./integration keeps the bare names (#4572) The four #4535-C1 baseline rows were the #4411 trap in cross-form: ./api's WebhookEventSchema was a z.object (OpenAPI 3.1 webhook definition) while ./integration's is a z.enum of connector event types — same names, two concepts, and which one you got depended only on the import path. Import-statement-level scan across framework, cloud and objectui: zero external consumers on either side (each pair's only importer is its own unit test; cloud and objectui reference neither name). - api WebhookConfig(Schema): DEAD — wired into nothing, not even RestServerConfigSchema; no runtime reads a REST webhook config. Deleted (major window). Its authorable-surface lines deleted by hand per the #4458/#4568 precedent (plugin-config type, not authorable metadata — no tombstone, no D2 conversion). - api WebhookEvent(Schema): renamed OpenApiWebhookEvent(Schema) — it is the OpenAPI 3.1 top-level `webhooks` descriptor and now sits in the existing OpenApi* family; OpenApi31ExtensionsSchema wiring updated, authored shape unchanged. - integration WebhookConfig/WebhookEvent: untouched, now sole owners of the bare names, so the pair stays one coherent family in one domain. dual-source-exports.baseline.json: exactly the 4 named rows removed (35 → 31). json-schema.manifest: api/WebhookConfig retired, api/WebhookEvent → api/OpenApiWebhookEvent (deliberate rename, not a silent drop). Changeset: @objectstack/spec major with FROM → TO lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
…:generated --fix, 2 proved stale) api-surface.json: -WebhookConfig(Schema)/-WebhookEvent(Schema) on ./api, +OpenApiWebhookEvent(Schema). Reference docs: the api/connector.mdx page (which documented only the removed ./api pair, under a misleading name) is no longer emitted; rest-server.mdx now documents OpenApiWebhookEvent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
…arrel import timed out under parallel turbo load
typeof import('./rest-server.zod') is type-level only; if a bare
WebhookEventSchema/WebhookConfigSchema export returns, the conditional
type flips to `true` and `tsc --noEmit` fails the false assignment.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
August 2, 2026 08:49
os-zhuang
enabled auto-merge
August 2, 2026 08:49
This was referenced Aug 2, 2026
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Aug 3, 2026
… ./api,./kernel 侧死删 (objectstack-ai#4587) (objectstack-ai#4603) * feat(spec)!: resolve the MetadataEvent/MetadataBulkRegisterRequest dual source — ./kernel copies removed, ./api keeps the bare names (objectstack-ai#4587) The three objectstack-ai#4535-C2 baseline rows were the objectstack-ai#4411 trap on the kernel metadata family: MetadataEvent(Schema) and MetadataBulkRegisterRequestSchema each had a second, different declaration in ./kernel, and which shape a consumer got depended only on the import path. Import-statement-level scan across framework, cloud and objectui: - ./kernel copies: zero importers outside their own unit test in all three repos. The kernel MetadataEvent lifecycle vocabulary (metadata.registered/.../exported) has NO producer anywhere; the kernel bulk-register per-item `namespace` field matches no enforced write path (IMetadataService.bulkRegister and MetadataManager.bulkRegister both take {type,name,data} items only). - ./api MetadataEvent is the live realtime contract: MetadataManager publishes metadata.{type}.{created|deleted} events and @objectstack/client / client-react subscribe against the type. - ./api MetadataBulkRegisterRequestSchema is the POST /api/meta/bulk/register contract whose item shape matches the runtime. Disposal (route 1, dead-side delete, v17 major window): both kernel copies removed; ./api is the sole owner of the bare names. Name continuity for the kernel-only type alias: `MetadataBulkRegisterRequest` (z.input) is now exported from ./api beside its schema, per the family convention and the objectstack-ai#4570 docs-import concern. Compile-time pin (typeof import conditional type, objectstack-ai#4581 pattern) keeps the bare names out of ./kernel; new events.test.ts covers the surviving ./api declarations. dual-source-exports.baseline.json: exactly the 3 named rows removed (31 -> 28). json-schema.manifest: kernel/MetadataEvent and kernel/MetadataBulkRegisterRequest retired deliberately; their 11 authorable-surface rows hand-deleted per the objectstack-ai#4458/objectstack-ai#4568/objectstack-ai#4581 precedent (plugin TS types, schema bodies deleted — no silent-strip path, misuse fails at the import site at compile time). Changeset: @objectstack/spec major with FROM -> TO migration lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL * chore(spec): regenerate api-surface + reference docs for objectstack-ai#4587 (check:generated --fix, 2 proved stale) api-surface.json: ./kernel loses MetadataEvent(type)/MetadataEventSchema and MetadataBulkRegisterRequest(type)/MetadataBulkRegisterRequestSchema; ./api gains the MetadataBulkRegisterRequest type alias beside its schema. Reference docs: the api/metadata-plugin.mdx page (which documented the removed kernel pair under the api section and advertised a then-nonexistent ./api type import) is no longer emitted; api/events.mdx now documents MetadataEvent, api/metadata.mdx documents MetadataBulkRegisterRequest, and kernel/metadata-plugin.mdx drops the removed schemas. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL * chore(spec): regenerate reference docs after merging main's import-surface gate (objectstack-ai#4595) The merge brought in build-docs' new import-surface ratchet. This PR's MetadataBulkRegisterRequest type alias on ./api closes the gap the fresh baseline had accepted, so its line is deleted (shrink-only ratchet). --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Aug 3, 2026
…ai#4610) (objectstack-ai#4638) * feat(spec)!: resolve the notification-vocabulary dual sources — ./ui Notification(Config) removed, ./system NotificationConfig removed, ./api keeps the bare names (objectstack-ai#4610) The four objectstack-ai#4535-C3 baseline rows were the objectstack-ai#4411 trap on the notification vocabulary: Notification(Schema) had a second declaration in ./ui diverging from ./api, and NotificationConfig(Schema) had two declarations (./system vs ./ui) that shared nothing but the name. Import-statement-level scan across framework, cloud and objectui: - ./api Notification(Schema) is the live REST inbox-row contract: embedded in ListNotificationsResponseSchema, part of NotificationProtocol, implemented by @objectstack/client, served by the runtime notifications domain, and mirrored by contracts' InboxNotification (ADR-0030: the bell reads this shape). - ./ui Notification(Schema) — a toast/banner instance shape — had zero importers outside its own unit test; objectui pins only the presentation enums (NotificationType/Position/ActionSchema), which stay. - ./system NotificationConfig(Schema) — a channel+template+recipients+ schedule+retryPolicy+tracking wrapper — had zero importers, is wired into no parent schema, predates ADR-0030's accepted delivery model (NotificationService.emit / NotifyConfigSchema / sys_* objects), and advertised unenforced capability (objectstack-ai#3197 dead-letter channels). - ./ui NotificationConfig(Schema) — a toaster global config — had zero importers. Disposal (route 1, dead-side delete, v17 major window): the ./ui pair and BOTH NotificationConfig declarations removed; ./api is the sole owner of the bare Notification(Schema) names and NotificationConfig left the export surface entirely. Compile-time pins (typeof import conditional type, objectstack-ai#4581 pattern) keep the bare names out of ./ui and ./system; the surviving ./api declaration is already covered by api/protocol.test.ts. dual-source-exports.baseline.json: exactly the 4 named rows removed (28 -> 24). json-schema.manifest: system/NotificationConfig, ui/Notification and ui/NotificationConfig retired deliberately; their 25 authorable-surface rows hand-deleted per the objectstack-ai#4458/objectstack-ai#4568/objectstack-ai#4581/objectstack-ai#4603 precedent. api-surface + reference docs regenerated via check:generated --fix (2 proved stale; the api/notification.mdx page folds into api/protocol.mdx where the declaration lives). docs-import-surface baseline (objectstack-ai#4595): untouched. Changeset: @objectstack/spec major with FROM -> TO migration lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL * docs(audits): 严格性台账跟随 objectstack-ai#4610 —— ui/notification.zod.ts 站点 3 → 1 删除 ./ui 的 Notification / NotificationConfig 两个形状后,台账里 `notification.zod.ts` 那行声明的站点数过期(gate 点名 ledger:454: declares 3 site(s), found 1)。把它从「3 ea」的合并行拆出单列为 1, 并写明为何掉了两个站点;`ui/` 章节总计 200 → 198 相应收敛。 check:strictness-ledger 恢复绿(67 文件 / 5 目录,站点数与章节总计均衡); 同 job 的其余源码审计(liveness / empty-state / variant-docs / exported-any / react-declaration-parity / skill-examples)一并复跑通过。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Aug 3, 2026
…i#4641) (objectstack-ai#4643) `Session` / `SessionSchema` 各有两处声明,一处在 `api/auth.zod.ts`,一处在 `identity/identity.zod.ts`。消费者拿到哪个形状只取决于 import 路径(objectstack-ai#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 不可能失败(已另立 objectstack-ai#4642 记录,影响 objectstack-ai#4581/objectstack-ai#4638 已落地的 pin)。 本 PR 的断言经过 sabotage 验证:把声明加回去,测试立刻红。 连带更新:json-schema.manifest 去掉 identity/Session;authorable-surface 去掉 该 schema 的 10 个 key(整形状移除,同 objectstack-ai#4638 先例);api-surface 重新生成。 reference docs 跟着声明走 —— `Session` 现在文档化在 `references/api/auth` (真正声明它的模块)上,名字碰撞产生的 `references/api/identity` 页随之消失。 严格性台账 `identity/` 粗粒度行 34 -> 33 并写明掉站点的原因。 docs-import-surface 基线未触发。 Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4572
#4535 C1:基线 35 条中的 4 条 ——
WebhookConfig(Schema)/WebhookEvent(Schema)在./api与./integration各有一份不同声明,且是跨形态的(./api的WebhookEventSchema是 z.object,./integration的是 z.enum)—— 选错 import 路径连"形状重叠、编译通过"的掩护都没有(#4411 陷阱)。判真源:三仓 import 语句级扫描
按手册以 import 语句级(非名字出现次数)扫描 framework、
/workspace/cloud、objectui(浅克隆,HEAD022e4c3):app-shell/clientValidation.ts一行注释提及WebhookEventSchema,非消费);rest-server.test.ts/connector.test.ts)。文件内部引用:./api侧WebhookEventSchema被OpenApi31ExtensionsSchema.webhooks引用;./integration侧被ConnectorSchema.webhooks(Audit: several event/subscription/connector enums are schema-only (declared, no runtime consumer) #3197,declared-not-enforced)引用。逐字段 diff(判定依据)
WebhookEvent(Schema)—— 两个概念,两种形态:./api./integrationz.objectz.enumwebhooks的定义描述子:name(snake_case)/description/method(默认 POST)/payloadSchema($ref)/headers?/security(hmac_sha256|basic|bearer|api_key)record.created/updated/deleted、sync.started/completed/failed、auth.expired、rate_limit.exceededWebhookConfig(Schema)—— 两个概念,且./api侧已死:./api./integration{enabled=false, events: WebhookEvent[], deliveryConfig{maxRetries=3, retryIntervalMs=5000, timeoutMs=30000, signatureHeader='X-Signature-256'}, registrationEndpoint='/webhooks'}WebhookSchema(automation).extend(events?,signatureAlgorithm?='hmac_sha256')RestServerConfigSchema,也没有任何 runtime 读 REST webhook 配置ConnectorSchema.webhooks(#3197)+ 单测处置(逐名判定)
./apiWebhookConfig/WebhookConfigSchema→ 死侧删除(手册路线 1,v17 major 窗口)。三仓零消费方、未接线进任何 schema、无 runtime。authorable-surface 的 4 行按 refactor(spec)!: remove the kernel metadata-loader envelope family — 11 names declared twice with different shapes (#4411) #4458/feat(spec)!: contracts 手写 interface 与域内 zod 推导类型收敛(3 簇 11 名,#4535·A3) #4568 先例手工删除(插件 TS 配置类型,非可作者化 metadata 文件 —— 不适用 tombstone / D2 conversion;schema 本体已删,不存在"父 schema 静默剥离"路径,误用者在 import 处编译期即失败,changeset 带 FROM → TO)。./apiWebhookEvent/WebhookEventSchema→ 改名OpenApiWebhookEvent(Schema)(手册路线 3,先例 [#4535·B] 跨形态同名三条:ShareRecipientType(type≠const)、TransformType(const≠type)、suggestFieldType(双实现 function) #4539 → PR feat(spec)!: 跨形态同名三条收敛 — ShareRecipientType / TransformType / suggestFieldType (#4539) #4571)。它是 OpenAPI 3.1 webhook 描述子,归入./api现有OpenApi*家族(OpenApiSpec/OpenApiServer/OpenApiSecurityScheme…);OpenApi31ExtensionsSchema.webhooks同步改引用,作者可写形状零变化(纯改名,无字段增删,消费方类型不变 —— "收敛 ≠ 无行为变化"逐字段核实过:此处确实无行为变化)。./integration两对保持原名不动,成为裸名唯一属主 ——WebhookConfig/WebhookEvent在同一域内保持成对语义(config.events: WebhookEvent[]),不再出现"裸名拆到两个域"的新陷阱。回归防护:编译期 pin(typeof import条件类型,tsc --noEmit兜底)+check:dual-source-exportsgate 本体(任何一侧再长回裸名即报新增双源)。api/WebhookConfig蓄意退休删行、api/WebhookEvent→api/OpenApiWebhookEvent(蓄意改名,非静默下架)。基线
dual-source-exports.baseline.json恰好删除 gate 点名的 4 行(35 → 31,不多删不漏删):WebhookConfig — [./api (type)] ≠ [./integration (type)]WebhookConfigSchema — [./api (const)] ≠ [./integration (const)]WebhookEvent — [./api (type)] ≠ [./integration (type)]WebhookEventSchema — [./api (const)] ≠ [./integration (const)]验证
pnpm --filter @objectstack/spec build✅check:dual-source-exports✅(self-test 通过;4308 名字 / 16 入口,基线 31)check:generated8/8 up to date ✅(api-surface + reference docs 经--fix定向再生,单独 commit;api-surface 净变化:−4 +2)typecheck✅;spectest287 文件 / 7270 用例全过 ✅pnpm build71/71 ✅、pnpm typecheck122/122 ✅、pnpm test132/132(spec 287 文件全过;pin 测试改为编译期后全绿) ✅Changeset
@objectstack/specmajor:两条 FROM → TO 迁移各带一行修法(./apiWebhookConfig删除 → 指向 automationWebhook/ integrationWebhookConfig并说明形状差异;./apiWebhookEvent→OpenApiWebhookEvent或按实际所需形状改从./integrationimport)。不改content/docs/releases/。范围外发现
RestServerConfig.openApi31整块(OpenApi31Extensions/Callback/OpenApiWebhookEvent)declared ≠ enforced ——packages/rest的normalizeConfig不读它,/openapi.json由静态 spec enrich,gen:openapi也不消费;ADR-0049 enforce-or-remove 候选。本 PR 只消歧命名、不裁决该块去留。🤖 Generated with Claude Code
https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
Generated by Claude Code