…` from `AlertDialogSchema`
Executes the maintainer ruling of 2026-09-10 on objectui#7963: all three retire
from `AlertDialogSchema` on both faces under ADR-0049 enforce-or-remove.
`cancelText` / `actionText` are the surviving spellings, and `confirmVariant`
has no survivor at all.
Measured on `72bcd7783` with a point-access probe: `schema.cancelLabel`,
`schema.confirmLabel` and `schema.confirmVariant` each score 0 tree-wide,
against firing controls on the very renderer under test —
`schema.cancelText` = 15 (read at `alert-dialog.tsx:37`) and
`schema.actionText` = 5 (read at `:38`). A bare-word probe would have lied
towards "live": every other owner of these spellings is a live key on a
different declaration, and none of them is an `AlertDialogSchema`.
A NAMED REFUSAL, not a deletion. `BaseSchemaCore` ends `.passthrough()` and the
TypeScript `BaseSchema` closes with `[key: string]: any`, so a dropped member
key is KEPT, not refused — deleting the declarations alone would have left the
silent accept exactly as it was. Each key stays declared and unwritable:
`retirementTombstone()` on the zod face, `?: never` on the TypeScript face.
`confirmVariant`'s message says plainly that it has no surviving spelling and
rules `cancelText` / `actionText` out by name — they are the footer's two
labels, not a variant. No replacement was invented.
Both existing pins are re-pointed rather than deleted: objectui#7104's
`INERT_DECLARED` becomes `RETIRED_DECLARED` and its type-level legs flip from
`string | undefined` to the `?: never` reading, and the objectui#7963 liveness
pin keeps every DOM reading while gaining the closure leg tying the refusal to
the measurement that justified it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
Fixes #7963
Executes the maintainer ruling of 2026-09-10, whose provenance is recorded on the card at #7963 (comment) (verbatim words, the relay named, and why decision batch #70's contrary
confirmVariantrow —5565626809— is superseded: its premise was falsified by the 2026-09-09 probe, which measured the key dead. The capability batch #70 actually wanted, the red destructive confirm button, is preserved as objectui#8978). All three retire fromAlertDialogSchemaon both faces, ADR-0049 enforce-or-remove.cancelText/actionTextare the surviving spellings, andconfirmVarianthas no survivor at all.Clause-②: yes— this narrows a published accept set on@object-ui/types. ⛔ Stays draft; the PM lands it.The measurement, re-derived on this branch's own base
72bcd7783⛔ Not quoted from the card. A point-access probe, tree-wide:
schema.KEYreadscancelLabeltypes/src/overlay.ts:151+zod/overlay.zod.ts:62confirmLabeloverlay.ts:156+overlay.zod.ts:63confirmVariantoverlay.ts:161+overlay.zod.ts:64FIRING CONTROLS, same instrument, firing on the very renderer under test (
packages/components/src/renderers/overlay/alert-dialog.tsx):schema.cancelText= 15 (read at:37),schema.actionText= 5 (read at:38). The three zeros are readings of the same file the controls light up — not a probe that failed to run.⛔ A bare-word probe would have lied, and towards "live". These spellings are overloaded across this tree and every other owner is a live key on a different declaration:
FormSchema.cancelLabel(types/src/form.ts:1243, read atform.tsx:1063,3266),objectql.ts:508,plugin-designer/src/components/ConfirmDialog.tsx:26,28(React props),plugin-grid/src/components/BulkActionDialog.tsx:552, andplugin-form'sModalForm.tsx:579/DrawerForm.tsx:528, which build a localcancelLabelfromschema.cancelText— the opposite direction. None is anAlertDialogSchema; none is touched, and a pin asserts it.The rest-spread near-miss is closed by measurement, not by reasoning. The keys do reach the primitive (not on
SchemaRenderer's strip list, so they ridecomponentPropsinto the renderer's rest-spread onto the Radix root) — the same channel that madeCollapsibleSchema.openlive in objectui#8236. The DOM reading settles it: theAlertDialogroot renders a context provider, not an element, so an unknown prop is dropped before reaching any node.What landed
A named refusal, ⛔ not a bare deletion.
BaseSchemaCoreends.passthrough()and the TypeScriptBaseSchemacloses with anany-valued index signature, so a dropped member key is kept, not refused — deleting the declarations alone would have left the silent accept exactly as it was and thrown the diagnostic away with it. Each key stays declared and unwritable:retirementTombstone()on the Zod face,?: neveron the TypeScript face, in the shapepage-actions-refusal-7926/page-breadcrumbs-refusal-8871established.cancelLabelcancelTextconfirmLabelactionTextconfirmVariantconfirmVarianthas no surviving twin, and its message says so plainly rather than pointing at a key that does not do the same job: it namescancelText/actionTextonly to rule them out ("those are the footer's two LABELS, not a variant"), states that the node declares no variant key at all, and sends the question to its own card. ⛔ No replacement was invented.The two existing pins are RE-POINTED, ⛔ not deleted
packages/types/src/__tests__/alert-dialog-read-dialect-7104.test.ts— itsINERT_DECLAREDbecame wrong in its own terms (they are now refused, not merely inert), so it isRETIRED_DECLARED; the three type-levelStillDeclaredlegs flip fromstring | undefinedto the?: neverreading and gain an authoring-site leg where the refusal actually bites; the@defaultdocblock leg — which asserted the shipped type promised a default nothing applied — is replaced by one asserting the docblocks now teach the retirement. objectui#7104 wrote that block so that this PR would re-derive it deliberately; that is what happened, and it went red exactly there.packages/components/src/__tests__/alert-dialog-footer-keys-liveness-7963.test.tsx— the liveness measurement this card rests on. Every DOM reading is kept: a retirement does not retire the evidence that justified it. Re-pointed from "this reading is the ruling's INPUT" to the ruling as taken, plus the note that matters most here — the renderer does not validate, so "a document written strictly against the retired keys renders an empty footer" is still true of a raw node; what moved is the gate one step earlier. A closure leg ties the two halves together in one place.Ablations — five legs, every mutation proved on disk BEFORE any result was read
Each leg: anchor
grep -con injected and removed text, agit hash-objectdiffering from the HEAD blob, restore viagit checkout HEAD -- ABSOLUTE_PATHfrom atrap … EXIT INT TERM, and restoration proved by blob hash plus an emptygit diff HEAD— ⛔ never by an exit code. Directions were predicted in the harness before the first leg ran; all five matched.cancelLabel10bb1d06…a9bbee6b…confirmLabel10bb1d06…1bf35c50…confirmVariant10bb1d06…78a44712…cancelLabel?: neverback to?: string69b03a6e…86082837…packages/typestype-check exit 2,TS2578 Unused '@ts-expect-error'in both pin files, pluszod-mirror-paritydrift77968a92…5d473285…WIRED,CHANNELandVARIANT_INSTRUMENTcontrols all red⭐ The three deletion legs are the argument for the mechanism, not just for the pin: with the arm deleted,
AlertDialogZod.shapeloses the key (34 members, nocancelLabel) and the retired document's issue paths drop to['confirmLabel', 'confirmVariant']— the key is silently accepted again. That is precisely the silent accept a bare deletion would have shipped.Tree state after the harness:
git status --porcelainempty, CLEAN.Verification — exit codes captured into files BEFORE any pipe
All at
415803b7a.vitest run --root . packages/types/pnpm --filter @object-ui/components testpnpm --filter @object-ui/types --filter @object-ui/components type-checktsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.jsonandtsc --noEmit && tsc -p tsconfig.test.json, so the filter matched real scriptspnpm --filter @object-ui/types --filter @object-ui/components linteslint .for both. ⭐ Read for the literal word:✖ 278 problems (0 errors, 278 warnings)(types) and✖ 948 problems (0 errors, 948 warnings)(components). Zero column-anchorederror-severity rows in the whole log; everyerrorstring in it is inside a warning message body or a filename. My five files lint with no output at all.check-changeset-fixed/check-changeset-no-major/check-changeset-presencecheck-control-bytesturbo run build --filter='!@object-ui/site' --concurrency=2check:doc-snippets/check:doc-examples/check:doc-typescheck:spec-symbols,check:handler-key-reads,check:sdui-registration-pinscheck:doc-snippets,check:doc-examplesandcheck:sdui-registration-pinsreturned exit 2 = PREREQUISITE NOT MET ("run the build first" / "this is exit 2, not a pass"). Those are NOT MEASURED, not red — they were re-run green after the whole-tree build, and are reported from that run.Declared narrowing: the repository-wide
pnpm lintandturbo run type-checkacross all 47 projects are CI's runs, not run here. The narrowing's downstream risk is instead answered by measurement: no fixture, catalog schema, example app, doc fence or template authors any of the three on analert-dialognode anywhere in the tree, so no shipped document is stranded — and the whole-tree build plus 638 doc snippets compiling against the emitted.d.tsis the consumer-side reading.Changeset
minor— AGENTS.md:240: a breaking change in this repo isminor(amajorwould drag the whole 39-package fixed group off@objectstack's cadence). Precedent objectui#5905, which likewise carriesClause-②: yes.@object-ui/types: minor,@object-ui/components: patch. ⛔content/docs/releases/untouched.验收备注
noted, not filed — objectui#8871's tree-scoped point-access probe (
nothing READS .breadcrumbs) fires on a prose mention in any file outside its named exclusion list, and it caught one in this PR's own new docblock, which cited the precedent asPageNodeSchema.actions/ that key spelled with a leading dot. Fixed here by spelling both arms without the leading dot and leaving a note beside them saying why. Not filed: the pin's tree-scoped shape is deliberate and correct (a file-scoped check would miss the reader nobody thought of), and the friction is one line of prose per citing card. Successor who meets it: the next retirement card that cites that precedent in a docblock — which is a real successor, since these refusal cards routinely cite each other.🤖 Generated with Claude Code
https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
Generated by Claude Code
Generated by Claude Code