Repository navigation
Description
Observed while running the per-surface identifier census for #12245. Filed unassigned as a finding, not a defect claim.
What was observed
EventNameSchema (packages/spec/src/shared/identifiers.zod.ts) is bound by exactly
three schemas, and by nothing else in either repository:
| Binding | Site | Runtime consumer? |
|---|---|---|
EventTypeDefinitionSchema.name |
kernel/events/core.zod.ts:94 |
none |
EventSchema.name |
kernel/events/core.zod.ts:122 |
none |
EventMessageSchema.eventName |
api/websocket.zod.ts:299 |
none |
Reproduced on origin/main at e2debee6: outside kernel/events/core.zod.ts and
api/websocket.zod.ts themselves, every reference to those three names is a generated
baseline (api-surface/*.json), the file's own test, type-alias-convention.pin.test.ts,
CHANGELOG.md, or V3_MIGRATION_GUIDE.md. No plugin, service, driver, or app parses
through any of them.
What the platform actually validates event names with is a closed literal enum that
does not reference EventNameSchema at all — packages/spec/src/api/events.zod.ts:
export const DataEventType = z.enum([
'data.record.created',
'data.record.updated',
'data.record.deleted',
]);
export const BulkDataEventType = z.enum([
'data.records.updated',
'data.records.deleted',
]);grep -c EventNameSchema packages/spec/src/api/events.zod.ts returns 0.
The sibling free-text surface is unconstrained rather than schema-guarded:
WebSocketEventSchema.channel is a bare z.string() (api/websocket.zod.ts:468) whose
own describe() documents "record.account.123" and "user.456" as legitimate channel
spellings.
Why it may matter
EventNameSchema is presented as the platform's event-name grammar — the docblock on
SystemIdentifierSchema lists "Event keys" among the surfaces it governs, and
EventNameSchema carries its own worked examples. In practice it governs nothing that
runs: the real vocabulary is closed and enumerated, and the one open event-ish surface
(channel) opted out into z.string().
That makes it the ADR-0049 declared-but-unenforced shape, with the usual consequence for
AI-authored metadata: a generator that reads EventNameSchema and emits an event name
satisfying it has satisfied nothing the platform will check, while a generator that emits
anything outside the closed enum is refused by a rule the identifier file never mentions.
Two routes, neither obvious and neither recommended here:
- Bind it — have the open event surfaces (starting with
channel, and any future
custom-event registration) run throughEventNameSchema, making the declared grammar
the enforced one. - Retire it under ADR-0049 enforce-or-remove, and let
DataEventType/
BulkDataEventTypestand as the only event-name contract. This is a published-export
removal, so it takes the ADR-0087 conversion.
Recording the measurement only; sizing and the route are a maintainer call.
Related: #12245 (the census that surfaced this) · #4673 (the same class, on
DataEventType's retired data.field.changed member) · ADR-0049.
Activity
os-warren commentedon Aug 31, 2026
分诊定级 →
domain:spec·needs-user-decision· p3 · tooling。摘finding。锚定。
packages/spec/src/shared/identifiers.zod.ts及两条路线的落点(kernel/events/core.zod.ts·api/websocket.zod.ts·api/events.zod.ts)全在packages/spec⇒domain:spec。为什么交你裁。 ADR-0049 已经把原则定死(声明而不强制的必须强制或移除),所以要裁的是路线,而两条都动公共契约:路线 1(绑定
channel等开放事件面)是 accept 集收窄;路线 2(退役)是已发布 export 的移除,须走 ADR-0087 conversion。⛔ 分诊不代裁。定级 p3:无行为缺陷、无错误答案上线。危害在 ③ 轴且是真的 —— 一个读
EventNameSchema并据以生成事件名的 generator 满足了一个平台根本不检查的东西;而它若生成闭合枚举之外的任何名字,会被一条 identifier 文件从未提及的规则拒收。两个方向都误导,这是比单纯「死代码」更贵的形状。⭐ 与 #13612 是同族,建议同一次裁决一并给方向 —— 但不要合卡
三张卡出自同一次 #12245 普查,是同一种病的三个面:
EventNameSchema有三个绑定 schema,但那三个零运行期消费者needs-user-decisionp3needs-user-decisionp3SystemIdentifierSchema散文声称覆盖 11 个面,实测只绑 1 个pm:queuep3(纯散文更正,零 accept 集变更)⇒ 裁 #13612 与本卡时,同一句「bind 还是 retire」可以覆盖两张,省你一次上下文切换。
⛔ 但两张不合并,因为 bind 路线的爆炸半径完全不同:#13612 的绑定会改 object name / field name 的 accept 集(内联正则允许前导下划线,
SnakeCaseIdentifierSchema不允许 ⇒ 存量元数据可能被拒);本卡的绑定动的是WebSocketEventSchema.channel(今天是裸z.string())⇒ 收窄一个从未被约束过的自由文本面,风险与代价都是另一回事。一句方向、两张各自评估半径。派单/裁决时手上要有的三个读数(已复核口径,
origin/main @ e2debee6)EventTypeDefinitionSchema.name(kernel/events/core.zod.ts:94)·EventSchema.name(:122)·EventMessageSchema.eventName(api/websocket.zod.ts:299),三者均无运行期消费者;DataEventType/BulkDataEventType(api/events.zod.ts),且grep -c EventNameSchema packages/spec/src/api/events.zod.ts= 0;WebSocketEventSchema.channel(api/websocket.zod.ts:468)是裸z.string(),其describe()自己举的合法拼法是"record.account.123"/"user.456"——EventNameSchema的文法,是 bind 路线的第一道判据,裁决前值得实测一次,否则「绑定」可能当场拒掉平台自己文档里的示例。Refs:#12245(普查)· #4673(同类,
DataEventType已退役成员data.field.changed)· ADR-0049。Generated by Claude Code
os-support-ai commentedon Sep 1, 2026
Claim:
session_01Mciyv38maJ6HYVMiaM26T1(domain:spec execution seat, dispatch batch 3) · branchclaude/issue-13612-identifier-retirements· family-folded with #13612 per both cards' 2026-09-01 RULED comments (one dispatch, one PR, each member independently verifiable). Route: retire per the ruling —EventNameSchemaand its three bindings out under ADR-0049 enforce-or-remove + ADR-0087 conversion;DataEventType/BulkDataEventTypestand as the only event-name contract; ⛔WebSocketEventSchema.channelstays a deliberatez.string()— the ruling adds no constraint there. Premise re-verified onorigin/main @ c85a2657: exactly three binding sites (core.zod.ts:94,:122,websocket.zod.ts:299), no runtime consumer. Clause-② ⇒ contract-review tier; in-seat review before enqueue. ⛔ #13621 (the prose-correction sibling) is NOT part of this dispatch and proceeds after this lands.Generated by Claude Code
chore: changeset for the #13612/#13613 identifier-schema retirements
os-support-ai commentedon Sep 1, 2026
os-dev-report
{ "issue": 13613, "status": "done", "branch": "claude/issue-13612-identifier-retirements", "pr": "https://github.com/objectstack-ai/objectstack/pull/14158", "premise_still_valid": true, "summary": "Retired EventNameSchema and its EventName type per the 2026-09-01 RULED comment (director batch C): removed from shared/identifiers.zod.ts with a retirement note; the three binding fields (EventTypeDefinitionSchema.name and EventSchema.name in kernel/events/core.zod.ts, EventMessageSchema.eventName in api/websocket.zod.ts) stay and widen to plain z.string() with describes pointing at the closed enums; retired-defs row shared/EventName registered under protocol major 18 plus D3 semantic entry event-name-schema-retired; manifest key deleted behind the #4725 gate; Iso499 isomorphism pin left with the schema (836 to 835, ledgered in the pin file per house style). DataEventType / BulkDataEventType byte-for-byte untouched and stand as the only event-name contract; WebSocketEventSchema.channel untouched as a deliberate bare z.string() per the ruling. Premise re-verified on origin/main at c85a2657 and again on the merged tree: exactly three binding sites, zero runtime consumers. #13621 untouched (the Event-keys line in SystemIdentifierSchema's docblock is that card's surface). Family-folded with #13612 in one PR; this card's increment is commit 928a373a plus its share of the changeset.", "files": [ "packages/spec/src/shared/identifiers.zod.ts (EventNameSchema + EventName removed, retirement note added)", "packages/spec/src/kernel/events/core.zod.ts (two name fields to z.string(), retirement note)", "packages/spec/src/api/websocket.zod.ts (eventName to z.string(), retirement note, import dropped)", "packages/spec/src/shared/identifiers.test.ts (EventNameSchema block removed)", "packages/spec/src/kernel/events.test.ts (reject-side grammar fixture repinned to the widening)", "packages/spec/src/type-alias-convention.pin.test.ts (Iso499 pin removed, count 836 to 835, ledger comment added)", "packages/spec/src/migrations/entries/retired-defs/18.shared__EventName.ts (new)", "packages/spec/src/migrations/entries/semantic/18.event-name-schema-retired.ts (new)", "packages/spec/src/migrations/registry.ts + json-schema.manifest/shared.json + api-surface/declaration-map/export-origins + content/docs/references/** + skills/*/references/_index.md (regenerated)", "packages/spec/ZOD_SCHEMA_AUDIT_REPORT.md (hand-maintained line corrected)", ".changeset/identifier-schemas-declared-unenforced-retired.md (shared with #13612)" ], "gates_and_tests": "Full readings on PR #14158 (shared with #13612), all at merged head a46ef310: spec suite 448 files / 12033 tests all passed (the pre-merge lap caught the events.test.ts fixture that pinned the deleted grammar branch - triaged and repinned as an acceptance pin holding the widening); spec typecheck green; dispatch gate list plus derived union all PASS. NOT MEASURED locally, CI-owed by their own printed verdicts: dual-build-cjs-loads, type-check-debt, test-completeness, dev-prereqs. Skills diff is generated refs only: 8 _index.md files, per-file line counts unchanged, SKILL.md bundle sum 10537 to 10537.", "derived_judgments": [ "Replacement shape for the three binding fields: plain z.string() with a describe naming the closed enums as the platform-checked vocabulary - a widening, deliberately NOT a binding to the enums (EventTypeDefinitionSchema declares custom event types; binding would be a new constraint the ruling did not order). Declared per-field in the PR body for contract review", "events.test.ts reject fixture pinned exactly the deleted grammar branch; repinned as an acceptance pin over the previously refused spellings so a silent grammar regression goes red", "EventName had no EventNameParsed sibling and no external type consumers - type removal is TS2305-only", "ZOD_SCHEMA_AUDIT_REPORT.md treated as hand-maintained (last touched by #13895) and its EventNameSchema mention corrected" ], "semver": "@objectstack/spec minor + BREAKING banner (launch-window grade, the ruling's own words); shared changeset carries the adr-0087 marker registering event-name-schema-retired", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
os-support-ai commentedon Sep 1, 2026
Collection review: ACCEPT · Clause-② contract review: PASS
Reviewed PR #14158 at head
881b685f(this card's increment is commit928a373a, independently verifiable per the family rule). Tier fuse read before review:last_served_model = claude-fable-5(= CONTRACT_REVIEW_TIER); in-seat review per the 2026-08-31 ruling.Contract judgment list (this card's increment):
EventNameSchema+EventNameremoved fromshared/identifiers.zod.tswith a tombstone note;SystemIdentifierSchema/SnakeCaseIdentifierSchema/MetadataItemNameSchemabyte-untouched. The three binding fields (EventTypeDefinitionSchema.name,EventSchema.name,EventMessageSchema.eventName) stay and widen to plainz.string()with describes naming the closed enums — exactly the ruled shape (DataEventType/BulkDataEventTypestand as the only event-name contract; both byte-untouched in the diff).WebSocketEventSchema.channeluntouched (the ruling adds no constraint there). Every previously-valid document stays valid (pure widening, no stored break). The widening is pinned inevents.test.ts: spellings the retired grammar refused now must parse, so a silent regression to a schema-level grammar becomes a deliberate ruling rather than drift — a judgment I endorse.retired-defs/18.shared__EventName.ts+ D3 semantic entryevent-name-schema-retired; the Iso pin ledgered 836→835.Micro-patch audit and the NOT-governed (0/44, #11705-certified twice) landing reading are recorded on #13612's ACCEPT comment — same PR, same head.
PASS ⇒ stripping
needs:contract-review(both carriers) in this same stroke; ordinary queue landing proceeds (ready → auto-merge).Generated by Claude Code
test(spec): kernel test titles state each cited decision in words ins…