Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

Commit 40f34b4

Browse files
claude[bot]claude
andauthored
retire(types): remove the EventHandlersSchema published export (ADR-0049 enforce-or-remove) (#9552)
Fixes #6910 Retires the published export `EventHandlersSchema` (`z.record(z.string(), z.function())`) from `@object-ui/types` under ADR-0049 enforce-or-remove, per the maintainer ruling recorded on the card (decision batch #25, reconfirming batch #8 on objectui#6124). This is a **resume**. The branch already carried a WIP commit that deleted the export and the barrel line and left a NOTE in place; that commit was deliberately incomplete and red by construction. This PR merges `origin/main` into the branch (merge, never rebase), then finishes the remaining halves. ## What landed | # | File | Change | |:--|:--|:--| | 1 | zod base module | export + declaration already gone on the WIP; NOTE **corrected twice**, see below | | 2 | zod barrel | re-export line already gone on the WIP; verified | | 3 | `zod-mirror-parity.test.ts` | the self-contained two-line `EXCLUSIONS` entry for the removed const, deleted | | 4 | `handler-keys-json-refusal-6124.test.ts` | the `MIRROR_FILES` docblock sentence the removal falsifies, rewritten | | 5 | `.changeset/6910-retire-event-handlers-schema.md` | one changeset | ## The expected red, and its resolution Removing the export turns the stale-entry census in `zod-mirror-parity.test.ts` red with exactly `['base.zod.ts#EventHandlersSchema']` — that census reads exported consts off disk, so it fires for any route that removes the export. Edit 3 is what clears it. The whole package runs **190 files / 4515 tests, all passing**, with `type-check` green across all three `tsc` passes. The deleted row's reason string is shared verbatim with the sibling `HTMLAttributesSchema` entry, so the deletion was anchored on the key line, not on the reason text; the sibling entry is untouched and still present. ## Round 2 — contract review F1 (MAJOR), repaired The review reproduced 8 of 8 claims and added two checks of its own that strengthen this PR: a set difference over all 489 `EXCLUSIONS` keys (exactly one key removed, the sibling surviving at the identical address on both refs), and a red-first proof that re-adding the retired row turns the census red naming only that key — so the delete was mandatory, not a test weakened to go green. It also found that my rewritten NOTE had shipped a **born-false census sentence**: *"every authored `events` in the corpus is `TimelineSchema.events`, an unrelated array."* **The instrument behind that claim was itself the defect.** I had matched only the JSON-quoted `"events":` spelling, which structurally cannot see a TypeScript object literal, and then generalised the result to "the corpus". Re-measured on this branch over the tracked files under `packages/`, `apps/` and `examples/`, with a live control that fires and a clean absent-token control at zero, the object-literal form finds the node-level record shape the paragraph warns about — on nodes that are not timelines — plus a larger set of hook and validation string arrays. Of the files declaring a `type: 'timeline'` node, the only ones carrying an `events` key are the declaration itself and a refusal probe. **`TimelineSchema.events` is authored zero times.** The sentence was wrong in both directions. This was the defect the rewrite existed to remove, recurring inside its own fix, and it reached the emitted `.d.ts`. **No replacement count is written.** The paragraph now states the mechanism, which does not rot — nothing declares the key, nothing reads it, so whatever a document writes there no renderer runs it — points at objectui#9553 for the census, and records that it shipped a frozen census answer once already. The operative instruction is unchanged: `.passthrough()` ⇒ kept, judged by nothing, run by nothing, do not send an author there. ## Deviation from the WIP commit, stated rather than folded in The WIP NOTE told the reader that handlers are authored as `BaseSchema.events`. **That is false on this tree**, and it is the one thing in the inherited work that was not adopted as-is: `BaseSchemaCore` has 21 members and declares no `events`; the TypeScript `BaseSchema` interface declares none either; `schema.events` and `props.events` have **0** read sites across `packages`, `apps` and `examples`, against live controls `schema.className` and `schema.visible` firing in the same pass; and `@objectstack/spec` 17.4.0 declares no UI-node `events` keyed to actions. Shipping that sentence would have replaced one unauthorable surface with a pointer to a non-existent one, which is the exact hazard this card exists to remove. The NOTE now points at the real JSON face — `handlerKeyRefusal()` and the node-type spelling PR #6498 established. ## Consumer verification | repo | result | control | |:--|:--|:--| | objectui (re-taken here) | 5 occurrences / 5 files on `origin/main`, **zero composition consumers** | `BaseSchema` → 429 files, fires; absent-token → 0 | | objectstack (re-taken here) | **0** | `BaseSchema` fires in the same corpus | | cloud | **0** — ⚠️ INHERITED, not re-taken | live substituted control `@object-ui/*`, 16 files | | hotcrm | **0** — ⚠️ INHERITED, not re-taken | live substituted control `@object-ui/*`, 3 files | The cloud and hotcrm legs are carried forward from the published reading on objectstack#15886 and are **not** presented as readings this seat took; this container holds no access to either repo. Reverse verification against the rebuilt `dist` rather than a cache: the built zod barrel exposes **211** named exports, the removed symbol is absent from it, and three sibling controls (`HTMLAttributesSchema`, `BaseSchema`, `ClassNameStylePropsSchema`) all resolve. ## Acceptance notes **The changeset's level came from the gate's own verdict, not from a sibling card.** `check-changeset-presence` scored this change `@object-ui/types`, "Scored `minor` at most, never `major`", so the declaration is `minor`. **On the BREAKING banner (review F2), answered explicitly.** The reviewer is right that the distinction I originally drew from objectui#9040 — "that one had a live consumer face, this has none" — is **not supported**: 9040 also recorded a zero in-repo census and took the banner anyway, and both removals produce an external compile error. I withdraw that reasoning. The banner is nonetheless omitted, for a different and narrower reason: **this card's ruling names the banner as a conditional and the condition was not met.** The ruling states that a live import anywhere means "the ruling stays retire but the changeset becomes BREAKING and the removal needs ADR-0087's conversion path rather than a plain delete". The four-repo census came back zero, so the plain-delete branch is the one the ruling itself selects. That is a ruled branch, not a house-style judgement, and it is not mine to override; 9040 is a different card under its own ruling. The body still tells a consumer plainly that an external import breaks and gives the FROM/TO, which is what a CHANGELOG reader needs either way. **The fifth occurrence is left alone, and three instruments agree.** `.changeset/6124-handler-keys-json-refusal.md` says "`EventHandlersSchema` is objectui#6910's card". That is a scope statement about what objectui#6124 did not cover, it stays true after the removal, `check-changeset-overwrite` exists precisely to report editing a changeset you do not own, and AGENTS.md #11 forbids re-addressing a published record. Not touched. **Dist residue (review F3), corrected count.** The removed symbol survives in the built output **3 times: 1 in `dist/zod/base.zod.d.ts` and 2 in `.js` files** (`zod/base.zod.js`, `zod/index.zod.js`) — all of them the NOTE's own prose carried through as a comment. No declaration and no export: a grep for either in `dist` returns 0. **Out-of-scope finding, filed rather than fixed here** — AGENTS.md's abridged JSON-protocol sketch publishes an optional `events` member typed as a record of event-name string to ActionSchema array as a `BaseSchema` member, and its action-system commandment authors one, while no schema declares the key and nothing reads it. Because `BaseSchemaCore` is `.passthrough()`, a node written from that sketch is kept by the parser, judged by nothing and run by nothing — a silent discard, and the carrier is the file agents read first. Filed as objectui#9553, whose own census has been corrected for the same instrument error described above. AGENTS.md is a governed surface and is not touched by this PR. **Gate readings.** Green here: the changeset family (presence, no-major, overwrite, claims), control-bytes, new-line-citations, handler-key-reads, spec-symbols, test-path-roots, unreferenced-sources, the package's `type-check`, and the package suite. `check:readme-exports` is **NOT MEASURED** locally — it exits 1 on an unbuilt tree and prints "the population COLLAPSED -- this run proves nothing", a prerequisite failure and not a finding; it needs a full repo build and belongs to CI. It cannot be moved by this diff in any case: **0** READMEs name the removed symbol, against control `BaseSchema` firing in 13 of 54 tracked READMEs. `check-governed-queue-guard` is CI-only; its surface list was read directly instead, and none of these paths is governed. **Clause-②: yes** — a published export is removed. The `needs:contract-review` carrier is the PM's to hang; no label was touched by this seat, on the card or here. Implemented-by: claude/issue-6910-retire-event-handlers-schema --- _Generated by [Claude Code](https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5495b8d commit 40f34b4

5 files changed

Lines changed: 94 additions & 9 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
'@object-ui/types': minor
3+
---
4+
5+
`EventHandlersSchema` is removed from `@object-ui/types` (objectui#6910).
6+
7+
**FROM** `EventHandlersSchema`, a `z.record(z.string(), z.function())` exported
8+
from the zod base module and re-exported from the zod barrel, **TO** *nothing*.
9+
There is no replacement export, and deliberately so.
10+
11+
**Nothing that worked stops working, because nothing could have worked.** Every
12+
value the record accepted had to be a function, so no JSON document could ever
13+
satisfy it, and nothing in this package composed it into a field. It could
14+
therefore neither admit a correct authoring nor refuse a wrong one — an author
15+
reading the published surface got no signal in either direction, which is the
16+
shape ADR-0049 enforce-or-remove exists to delete. Ruled by the maintainer on
17+
objectui#6124 (decision batch #8) and reconfirmed in batch #25.
18+
19+
**What to author instead.** Behaviour is authored as a NODE TYPE — an `action:`
20+
node with a declared action, the spelling PR #6498 established. The per-node
21+
`on*` handler keys that do exist are declared through `handlerKeyRefusal()`,
22+
which refuses every value, an authored object and a live function alike, and
23+
carries that same remedy in its own message. The function-valued face is a
24+
TypeScript prop shape and is untouched by this change: the `EventHandlers`
25+
interface is still declared and still exported.
26+
27+
**Not marked breaking, and that is a measurement rather than an assumption.** A
28+
consumer census for this symbol scored zero live imports in each of objectui,
29+
objectstack, cloud and hotcrm. The objectui and objectstack legs were re-taken
30+
on this branch under a positive control that fires in the same corpus; the
31+
cloud and hotcrm legs are carried forward from the published reading on
32+
objectstack#15886 rather than re-taken here. That is a reading taken at this
33+
commit and nothing re-derives it afterwards: a TypeScript consumer outside
34+
those four repositories that imported the symbol gets a compile error naming
35+
it, and the FROM/TO above is what such a consumer needs.
36+
37+
⚠️ **No gate reports a second export of this shape.** Both standing instruments
38+
are structurally blind to it — the `z.function()` census matches a
39+
`key: z.function(` spelling and a record's VALUE type is not a key, and the zod
40+
mirror parity ledger exempts index signatures by design. A NOTE at the former
41+
declaration site, not a gate, is what records the absence.

‎packages/types/src/__tests__/handler-keys-json-refusal-6124.test.ts‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -470,10 +470,12 @@ const RETIRED: readonly Site[] = [
470470

471471
const ALL_SITES: readonly Site[] = [...RUNTIME_SLOT, ...RETIRED];
472472

473-
/** The nine mirror files the census covers; `base.zod.ts` holds only
474-
* `EventHandlersSchema` (a record, objectui#6910's card) and no named key.
475-
* `objectql.zod.ts` joined with objectui#6576 — it declared no handler key
476-
* at all until `ObjectDataTableSchema.onRowClick`. */
473+
/** The nine mirror files the census covers; `base.zod.ts` is not among them —
474+
* it declares no named handler key. The record-valued `EventHandlersSchema`
475+
* that once stood there was never one either, and objectui#6910 retired it;
476+
* the NOTE left at its site records why no JSON-authorable handler record
477+
* replaces it. `objectql.zod.ts` joined with objectui#6576 — it declared no
478+
* handler key at all until `ObjectDataTableSchema.onRowClick`. */
477479
const MIRROR_FILES = [
478480
'complex.zod.ts',
479481
'data-display.zod.ts',

‎packages/types/src/__tests__/zod-mirror-parity.test.ts‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3187,8 +3187,6 @@ const EXCLUSIONS: Readonly<Record<string, string>> = {
31873187
"a bare vocabulary with no `.shape`; it is checked where a mirrored KEY declares it",
31883188
'base.zod.ts#HTMLAttributesSchema':
31893189
"an index signature, not a declared key set — there are no keys to compare",
3190-
'base.zod.ts#EventHandlersSchema':
3191-
"an index signature, not a declared key set — there are no keys to compare",
31923190
'complex.zod.ts#CalendarViewModeSchema':
31933191
"a bare vocabulary with no `.shape`; it is checked where a mirrored KEY declares it",
31943192
'complex.zod.ts#DashboardWidgetTypeSchema':

‎packages/types/src/zod/base.zod.ts‎

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -718,9 +718,54 @@ export const ComponentConfigSchema = ComponentMetaSchema.extend({
718718
export const HTMLAttributesSchema = z.record(z.string(), z.any()).describe('HTML attributes');
719719

720720
/**
721-
* Event Handlers
721+
* Event handlers — NOTE, not a declaration. There is deliberately no
722+
* event-handlers const here (objectui#6910).
723+
*
724+
* `EventHandlersSchema`, a `z.record(z.string(), z.function())`, stood at this
725+
* spot until objectui#6910 retired it under ADR-0049 enforce-or-remove
726+
* (maintainer ruling on objectui#6124, decision batch #8, reconfirmed in batch
727+
* #25). It was a published export of `@object-ui/types/zod` that **no JSON
728+
* document could ever satisfy** — every value it accepted had to be a function
729+
* — and that nothing in this package composed. It could therefore neither
730+
* admit a correct authoring nor refuse a wrong one: an author reading the
731+
* published surface got no signal in either direction, which is exactly the
732+
* shape ADR-0049 exists to remove.
733+
*
734+
* Why no JSON-authorable replacement can be written here, i.e. why this is a
735+
* NOTE and not a narrower schema: on the JSON face handlers are not values at
736+
* all. The per-node `on*` handler keys that do exist are declared through
737+
* `handlerKeyRefusal()` in `./tombstone.zod.ts`, which refuses EVERY value —
738+
* an authored object and a live function alike — and carries the remedy in
739+
* its own message: author behaviour as a NODE TYPE, an `action:` node with a
740+
* declared action, the spelling PR #6498 established. A record of function
741+
* values is that same unauthorable shape with the key set left open as well,
742+
* so narrowing it could only make the refusal harder to read. The
743+
* function-valued face is a TypeScript prop shape, declared as `EventHandlers`
744+
* in `../base.ts`; a zod mirror of it would mirror something that never
745+
* crosses the wire.
746+
*
747+
* ⚠️ `BaseSchema` declares NO `events` key, and this note asserted that it did
748+
* until the claim was checked against the tree. AGENTS.md's abridged protocol
749+
* sketch shows `events?: Record<string, ActionSchema[]>` and its action-system
750+
* commandment authors one, but `BaseSchemaCore` declares no such member and
751+
* nothing reads `schema.events`: whatever a document writes under that key,
752+
* no renderer runs it. `BaseSchemaCore` is `.passthrough()`, so such a node is
753+
* KEPT, judged by nothing and run by nothing. ⛔ Do not send an author there.
754+
* ⛔ No count of authored `events` keys is stated here — objectui#9553 carries
755+
* that census. A census answer frozen into a comment is the defect AGENTS.md
756+
* commandment #9 forbids, and this note shipped one once already: the first
757+
* version of this paragraph named a total that was wrong on the day it was
758+
* written, and it reached the emitted `.d.ts` before review caught it.
759+
*
760+
* ⚠️ Do not conclude from a clean sweep that no second one of these exists.
761+
* Both standing instruments are structurally blind to this shape: the
762+
* `z.function()` census matches the `key: z.function(` spelling and a record's
763+
* VALUE type is not a key, and `../__tests__/zod-mirror-parity.test.ts`
764+
* exempts index signatures by design ("no keys to compare"). This note, not a
765+
* gate, is what records the absence.
766+
*
767+
* Precedent of the same shape: objectstack#12009 / PR #13413.
722768
*/
723-
export const EventHandlersSchema = z.record(z.string(), z.function()).describe('Event handlers');
724769

725770
/**
726771
* The two CSS passthrough attributes a node exposes: a Tailwind class string and

‎packages/types/src/zod/index.zod.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ export {
8080
ComponentMetaSchema,
8181
ComponentConfigSchema,
8282
HTMLAttributesSchema,
83-
EventHandlersSchema,
8483
ClassNameStylePropsSchema,
8584
} from './base.zod.js';
8685

0 commit comments

Comments
 (0)