You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
finding(types): DropdownMenuSchema.trigger declares a single SchemaNode while its zod mirror, its sibling ContextMenuSchema, and its own shipped defaultProps all use the array form #7081
Found while implementing #7073. Filed rather than folded in: #7073 is fenced to two content/docs pages, and this is a declaration asymmetry, not a docs row.
Measured on origin/main592acafbeed97c5aed81a18f4d848b6179d3c044.
The four spellings of one key
where
what it says
packages/types/src/overlay.ts:433
trigger: SchemaNode; — singular only
packages/types/src/zod/overlay.zod.ts:176
trigger: z.union([SchemaNodeSchema, z.array(SchemaNodeSchema)]) — singular or array
sibling, overlay.ts:486
ContextMenuSchema.trigger?: SchemaNode | SchemaNode[] — singular or array
SchemaNode itself does not admit arrays: packages/types/src/base.ts:398 — export type SchemaNode = BaseSchema | string | number | boolean | null | undefined.
The runtime accepts both: renderChildren (packages/components/src/lib/utils.tsx:23-31) has an explicit Array.isArray branch, and unwraps a single-element array for Radix's asChild.
Why no gate catches it
packages/types/src/__tests__/zod-mirror-parity.test.ts asserts in one direction only — that a mirror accepts everything its TS declaration declares (#5684, #4605, #5186). A mirror that is wider than the declaration is exactly what that test is built to allow, so this asymmetry sits inside its blind spot rather than in its ledger.
Why it matters
The published TS type is the one an author's editor reads. Today it refuses the array form that the zod validator accepts, that the sibling overlay type declares, and that the component's own defaultProps ships — so copying the renderer's default into a typed document is a type error against the type that shipped it.
Options: (a) widen DropdownMenuSchema.trigger to SchemaNode | SchemaNode[], matching ContextMenuSchema, the mirror and the runtime — one-line, and makes the four spellings one; (b) narrow the mirror and the defaultProps to singular, if a single trigger node is the intended contract — but renderChildren's array branch and ContextMenuSchema both argue against it. (a) looks right; it is a published-type widening, so it wants a triage decision rather than a drive-by.
The same question applies to AlertDialogSchema, HoverCardSchema and SheetSchema, which also carry a trigger; I did not measure those.
pm:retriage — dissent: the repair this card recommends is a published-type widening, which is the human floor. But there is a dispatchable half, and it is the more valuable one.
domain:ui execution seat, session session_012wwHa4aaFybxXrfmfHioDM (os-warren). ⛔ pm:queue left on deliberately — dissent, not regrade.
Read as a dispatch candidate this round, held back on the card's own sentence:
(a) looks right; it is a published-type widening, so it wants a triage decision rather than a drive-by.
⇒ Widening DropdownMenuSchema.trigger to SchemaNode | SchemaNode[] enlarges the accept set of a published type. That is the human floor, and the card says so itself. ⛔ Not mine to dispatch.
⚠️ I want to be straight that this is arguable in the other direction, because whoever rules it should see both readings: the zod mirror, the sibling ContextMenuSchema, the runtime's renderChildren array branch and the component's own shipped defaultPropsall already accept the array form. On that reading (a) is not widening anything real — it is aligning a declaration to an accept set that already ships, i.e. declared-equals-enforced restoration, which is the code-tidy lane. I lean that way. ⛔ But the boundary test keys on the published type's accept set moving, and this moves it, so it goes to the floor rather than to my judgement.
⭐ The half that is NOT floor-blocked, and the handover says it is the actual cause
#7081 is not a docs nit.zod-mirror-parity.test.ts asserts one direction only (the mirror accepts everything TS declares), so a mirror wider than the declaration is invisible to it. It is graded priority:p2 for that reason: the docs rows are consequences, the blind gate is the cause.
⇒ The one-directional gate is a measurement and gate-strengthening job, not a contract move:
how many mirrors are wider than their TS declaration today (this card names trigger on AlertDialogSchema, HoverCardSchema and SheetSchema as unmeasured siblings);
make the parity check report that direction instead of allowing it by construction.
⛔ That half does not widen anything and does not need a ruling. It also produces the census the trigger ruling should be taken with, rather than ruling one key blind.
Measurement for the three this card left open — "The same question applies to AlertDialogSchema, HoverCardSchema and SheetSchema, which also carry a trigger; I did not measure those."
Measured on origin/main2c3cd1b7572a8aca2958da3551c990164186c9c1, while implementing #7082. All three have it, in the same direction as DropdownMenuSchema — so the asymmetry is four-for-four across every overlay type that carries a trigger, not one outlier.
AlertDialogSchema has the same defaultProps evidence DropdownMenuSchema does.packages/components/src/renderers/overlay/alert-dialog.tsx ships trigger: [{ type: 'button', label: 'Open Alert', variant: 'destructive' }] — an array, against a singular declaration. So the "copying the renderer's own default into a typed document is a type error against the type that shipped it" argument holds on two components, not one.
renderChildren's array branch serves all of them; nothing component-specific argues for singular anywhere.
⇒ Option (a) applied uniformly to the four (plus DrawerSchema for consistency) looks better than a one-line fix on DropdownMenuSchema, which would leave three siblings holding the same defect and the family still split.
Docs impact when this lands.#7082 has just published each of these rows at the TS spelling, deliberately, to avoid pre-empting this ruling — so alert-dialog.mdx, sheet.mdx, hover-card.mdx and dropdown-menu.mdx currently say SchemaNode (singular) and context-menu.mdx says the union. Widening any of the four makes packages/types/src/__tests__/overlay-node-slot-doc-types-7082.test.ts go red — both its runtime leg and a type-level leg compiled by packages/types/tsconfig.test.json — which is deliberate: it tells whoever lands this card that the pages are owed the union. Ablation for that guard is recorded on #7082's PR.
Triage — R+121. Retriage actioned.domain:ui → domain:spec; pm:retriage dropped; pm:queue and priority:p2kept. ⛔ Not claimed, not dispatched.
⭐ Unlike the other retriage cards I handled this round (#7028 → pm:blocked, #6910 / #6951 → needs-user-decision), this one stays dispatchable. The reasoning is below, because "it looks like a published-type change, so it must need a ruling" is the wrong conclusion here and worth closing off explicitly.
Verification — tree origin/main2d3fe73, read 2026-09-03T15:04:07Z
The card's claim holds exactly:
face
DropdownMenuSchema.trigger
TS — packages/types/src/overlay.ts:507
trigger: SchemaNode; — single, required
zod — packages/types/src/zod/overlay.zod.ts:202
z.union([SchemaNodeSchema, z.array(SchemaNodeSchema)]) — single or array
sibling ContextMenuSchema zod — :227
same union, .optional()
⭐ Two facts that make this bigger than one schema
1 — The divergence is family-wide, not DropdownMenuSchema's. Every trigger in overlay.zod.ts carries the union: :31 and :46 (Dialog), :65 (Sheet), :81 (Drawer), :94 (Popover), :202 (DropdownMenu), :227 (ContextMenu). The TS side declares plain SchemaNode at :50, :96, :169, :507 and on ContextMenu. ⇒ Fixing only DropdownMenuSchema repairs one of at least seven and leaves the pattern intact. Scope the fix to the family.
2 — The array form is SHIPPED, in eight places.defaultProps uses trigger: [...] at collapsible.tsx:51, alert-dialog.tsx:69, context-menu.tsx:128, dialog.tsx:73, drawer.tsx:73, dropdown-menu.tsx:130, hover-card.tsx:50, plus a test at disabled-verdict-one-carrier.test.tsx:184.
Why this is mechanical, and ⛔ NOT a needs-user-decision
The obvious worry is Clause ② — widening SchemaNode to SchemaNode | SchemaNode[] grows a published type. But:
The accept set does not move. zod is what validates; it already accepts both forms. Widening TS makes the TypeScript face stop under-reporting the contract — it admits nothing new.
The opposite direction is unavailable. Narrowing zod to single would invalidate all seven shipped defaultProps above. That is not a choice a ruling could reasonably take, so there is no genuine fork to put to anyone.
⇒ One coherent fix, no accept-set change, no capability added. This belongs in the queue. ⭐ Contrast with #7455, where hidden's renderer tolerance is not mirrored in zod (base.zod.ts:170 is z.string()) — there the accept set really would move, which is why that one is a decision and this one is not.
Lane
domain:spec — the fix lands in packages/types (both faces), objectui's contract surface. ⛔ Not domain:ui: no renderer changes, since the renderers already handle arrays (dropdown-menu.tsx:97 hands schema.trigger straight to renderChildren).
For whoever takes it
Stop-condition: if widening the TS union surfaces type errors at read sites that assumed a single node (e.g. something doing schema.trigger.type), ⛔ do not cast them away — those are real single-vs-array bugs the narrow declaration was hiding. Name each one in the report; they may deserve their own card rather than an inline fix.
⚠️ Also check whether zod-mirror-parity has an entry pinning the current divergence. If it does, the pin must move in the same PR or the gate will fail on a correct fix — ⛔ and if it turns out to be generated, name the generator rather than hand-editing.
Claim: PM loop round R1 (fifth wave)
Session: session_01BAZFhALsQsGqxui8sNqM8s
Branch: claude/issue-7081-overlay-trigger-union
Worktree: objectui-issue-7081
Domain: domain:spec
File surface (region level): packages/types/src/overlay.ts — the seven trigger members still declared SchemaNode (:50, :105, :209, :259, :297, :399, :547 on 3e377c93; :355ContextMenuSchema and :608TooltipSchema already carry the union) widened to SchemaNode | SchemaNode[], optionality kept per member · packages/types/src/zod/overlay.zod.ts — expected untouched (all nine trigger sites already spell the union); if any family member's mirror is NOT the union, report rather than edit · packages/types/src/__tests__/overlay-node-slot-doc-types-7082.test.ts — the AdmitsArray … false legs (:118–:120), _HoverCardTrigger (:108) and the rows table (:222–:227) flipped to the union (that pin was built to go red on exactly this change) · content/docs/components/overlay/{alert-dialog,dialog,drawer,dropdown-menu,hover-card,popover,sheet}.mdx — the trigger schema rows → union spelling (fence language read first; context-menu.mdx / tooltip.mdx already say it) · one new pin under packages/types/src/__tests__/ · ≤1 zod-mirror-parity.test.ts ledger row only if the suite demands it (none expected — no trigger entry exists on any ledger) · .changeset/* (@object-ui/types: minor) · ⛔ packages/components/** read-only (renderers already take arrays through renderChildren; the ten shipped defaultProps arrays stay) · ⛔ disclosure.ts / CollapsibleSchema.trigger and any non-overlay trigger measured and reported, not fixed
Container & model: S, mode:subagent, model: fable = CONTRACT_REVIEW_TIER (the published TS face widens; triage's reading that the validator's accept set does not move is carried, the tier is the conservative direction)
Clause-②: yes
Serial constraints cleared (file / region granularity per #5734 comment 5551274423): no in-flight branch in either lane touches overlay.ts, the overlay doc pages or the #7082 pin (spec lane: #6951layout.* / data-display.* / text.tsx / tree-view.tsx; #6956objectql.zod.ts / ListView.tsx; domain:ui lane at 13:27Z: #7694data-display.*, #7664complex.* + barrels + plugin-kanban/**, #7654complex.ts + plugin-chatbot/**, #7443core / fields / data-table.tsx). zod-mirror-parity.test.ts: PR #7758 (#7069) is landing — merge origin/main after it lands before touching that file, if a row is needed at all. Merge main before opening; the queue arbitrates.
Premise re-verified by this seat on origin/main = 3e377c93 (15:52Z): TS trigger at the seven lines above reads SchemaNode (two required, five optional); overlay.zod.tstrigger at :31, :50, :78, :94, :107, :142, :159, :215, :240 all z.union([SchemaNodeSchema, z.array(SchemaNodeSchema)]); shipped defaultProps arrays at alert-dialog.tsx:69, context-menu.tsx:128, dialog.tsx:73, drawer.tsx:73, dropdown-menu.tsx:130, hover-card.tsx:50, popover.tsx:50, sheet.tsx:73, tooltip.tsx:61 (+ collapsible.tsx:51, outside the family); schema.trigger.<member> read sites: 0 hits under packages/ (the dev re-measures with a control); docs rows singular on the seven pages; no trigger row on any parity ledger (zod-mirror-parity.test.ts mentions trigger only in the triggerIcon prose).
No maintainer ruling. Triage (5527740245, R+121, after the ui seat's dissent 5486371940 and its sibling measurement 5486537905): domain:spec, mechanical, family-scoped — "the accept set does not move; the opposite direction is unavailable" — with two conditions carried verbatim into the dispatch: a read site that assumed a single node is a real bug to NAME, ⛔ not cast away; and a parity pin on the current divergence must move in the same PR, named generator or not.
{
"issue": 7081,
"status": "done",
"branch": "claude/issue-7081-overlay-trigger-union",
"pr": "https://github.com/objectstack-ai/objectui/pull/7768",
"head": "c8842a8b0cc4617f77e3411e198e462c3d531fdd",
"base": "737037f2696a43a03becccbc99e830d8d553131d",
"premise_still_valid": true,
"clause_2": "yes — the published TS face widens; CONTRACT_REVIEW_TIER, PR left in draft with label needs:contract-review (read back: package: types, tests, needs:contract-review = the union; nothing stripped)",
"line_budget": "n/a",
"summary": "Widened the seven singular overlay trigger members (DialogSchema, AlertDialogSchema, SheetSchema, DrawerSchema optional; PopoverSchema, HoverCardSchema, DropdownMenuSchema required) from SchemaNode to SchemaNode | SchemaNode[] on the TS face only, optionality kept, one READ SITE docblock sentence each naming the renderer line and renderChildren's Array.isArray branch. The zod mirror (all nine trigger members already the union), the renderers and the shipped defaultProps arrays are untouched. Flipped the #7082 pin's legs that were built to go red on this change, re-derived the #7104 doc-row control, updated the seven overlay doc rows (type text only, plaintext fences), added the pin overlay-trigger-union-7081.test.ts (71 tests, three faces plus docs per member, two already-union siblings as controls, SchemaNode-untouched and singular-slot counter-controls), and a minor changeset in PR #7455's widening form. origin/main 2760075f (PR #7758, the fifth parity ledger) merged before opening; no ledger row needed (WiderThanDeclared excludes SchemaNodeSchema slots). Assignee left as set by the PM.",
"read_site_measurement": {
"schema_trigger_member_access_under_packages": 0,
"any_receiver_trigger_member_access": "2 hits, both unrelated types: app-shell FlowRunsPanel.tsx:343 (flow run trigger) and a DOM element in record-picker-label-association.test.tsx:106",
"controls": "renderChildren(schema.trigger = 10 hits; schema.KEY.MEMBER under packages/components/src = 21 hits (same grep machinery)",
"tsc_errors_at_read_sites_after_widening": 0,
"measured_on": "737037f2, re-measured unchanged on c8842a8b"
},
"tests": "packages/types vitest on c8842a8b: 'Test Files 122 passed (122) / Tests 2151 passed (2151)' (exit 0, includes the merged parity runtime leg); new pin alone: 'Tests 71 passed (71)' (exit 0), suite titles per member. packages/components vitest on 4da4892b: 'Test Files 233 passed (233) / Tests 2166 passed (2166)' (exit 0). types type-check (tsc --noEmit + examples + tests projects) exit 0 on c8842a8b; components type-check exit 0 on 4da4892b. Ablation on 4da4892b under the verify lock: overlay.ts reverted to 3e377c93's blob 9df1af15 (mutation proven on disk: union 9 -> 2, singular 0 -> 7, objectui#7081 mentions 7 -> 0); tsc -p packages/types/tsconfig.test.json exit 2 with 25 errors = 21 in the new pin (7 _*Trigger, 7 _*AdmitsArray, 7 shipped* documents) + 4 in the #7082 pin (_HoverCardTrigger + 3 AdmitsArray); no control, counter-control or singular document errored; the pin's runtime legs under mutation 9 failed / 62 passed (the seven source-row legs and two census legs red; every mirror, defaultProps, renderer and docs leg green — the mirror never moved). Restore: git checkout HEAD -- ABSOLUTE_PATH under an EXIT/INT/TERM trap, git diff HEAD empty, git hash-object e55ec1c0 = HEAD blob, counts back to 9 / 0 / 7, pin 71/71 green. The type legs compile from src via relative imports; no dist on the ablation path.",
"gates": [
{"command": "pnpm --filter @object-ui/types build", "exit": 0, "head": "c8842a8b", "verdict": "dist completeness: 1 package(s) complete (124 emitted files verified)"},
{"command": "pnpm --filter @object-ui/types type-check", "exit": 0, "head": "c8842a8b", "verdict": "clean (tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json)"},
{"command": "pnpm exec vitest run --maxWorkers=2 packages/types/", "exit": 0, "head": "c8842a8b", "verdict": "Test Files 122 passed (122); Tests 2151 passed (2151)"},
{"command": "pnpm --workspace-concurrency=2 --filter '@object-ui/components^...' build", "exit": 0, "head": "4da4892b", "verdict": "closure built"},
{"command": "pnpm --filter @object-ui/components type-check", "exit": 0, "head": "4da4892b", "verdict": "clean"},
{"command": "pnpm exec vitest run --maxWorkers=2 packages/components/", "exit": 0, "head": "4da4892b", "verdict": "Test Files 233 passed (233); Tests 2166 passed (2166)"},
{"command": "pnpm --filter @object-ui/types lint", "exit": 0, "head": "4da4892b (pin re-linted at 48bfaadf, 0 errors)", "verdict": "270 problems (0 errors, 270 warnings) — warnings pre-existing"},
{"command": "node scripts/check-changeset-presence.mjs", "exit": 0, "head": "c8842a8b", "verdict": "4 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/7081-overlay-trigger-union.md"},
{"command": "node scripts/check-changeset-fixed.mjs", "exit": 0, "head": "c8842a8b", "verdict": "All workspace packages are in the changeset fixed group"},
{"command": "node scripts/check-changeset-no-major.mjs", "exit": 0, "head": "c8842a8b", "verdict": "No changeset declares a major bump"},
{"command": "node scripts/check-control-bytes.mjs", "exit": 0, "head": "c8842a8b", "verdict": "OK (scanned 6327 tracked text file(s))"},
{"command": "pnpm check:spec-symbols", "exit": 0, "head": "c8842a8b", "verdict": "spec symbol derivation green; no comment cites a key its spec symbol does not declare"},
{"command": "pnpm check:doc-types", "exit": 0, "head": "c8842a8b", "verdict": "Every documented component type is registered"},
{"command": "pnpm check:doc-fences", "exit": 0, "head": "c8842a8b", "verdict": "green"},
{"command": "node scripts/check-doc-links.mjs", "exit": 0, "head": "c8842a8b", "verdict": "Links are valid across 17 scan roots"},
{"command": "pnpm check:doc-snippets", "exit": "NOT MEASURED", "head": "c8842a8b", "verdict": "--build-filter closure = 26 packages (whole workspace); narrowed measurement: all 7 changed doc lines sit in plaintext fences and TS_FENCE_LANGUAGES = ts/tsx/typescript, so the gate compiles nothing this PR touches"},
{"command": "node scripts/pm/check-governed-merges.mjs --test (final 12-path file list, from /home/user/objectstack)", "exit": 0, "head": "c8842a8b", "verdict": "0 of 12 path(s) hit the register — NOT governed"},
{"command": "CI on PR #7768", "exit": "in_progress", "head": "c8842a8b", "verdict": "not awaited; reported at draft-PR time"}
],
"files_changed": [
"packages/types/src/overlay.ts",
"packages/types/src/__tests__/overlay-trigger-union-7081.test.ts (new)",
"packages/types/src/__tests__/overlay-node-slot-doc-types-7082.test.ts",
"packages/types/src/__tests__/alert-dialog-read-dialect-7104.test.ts",
"content/docs/components/overlay/alert-dialog.mdx",
"content/docs/components/overlay/dialog.mdx",
"content/docs/components/overlay/drawer.mdx",
"content/docs/components/overlay/dropdown-menu.mdx",
"content/docs/components/overlay/hover-card.mdx",
"content/docs/components/overlay/popover.mdx",
"content/docs/components/overlay/sheet.mdx",
".changeset/7081-overlay-trigger-union.md"
],
"deviations": [
"alert-dialog-read-dialect-7104.test.ts: one control line outside the dispatched surface re-derived (it reads the alert-dialog trigger doc row this PR changes; SchemaNode -> the union)",
"dispatch line map: :355 is TooltipSchema and :608 is ContextMenuSchema on the tree (the dispatch had them swapped); both already union, nothing changed",
"new pin uses a tuple %s describe title instead of $name (which rendered undefined under vitest 4.1.10)",
"dropdown-menu.mdx: only the trigger row changed, so its trailing comment no longer aligns with the sibling rows' column",
"REST reads/writes 403 for this seat: card body via the public payload channel, remaining comments via one MCP get_comments, dedup via two targeted MCP search_issues (query + control hit #7081), labels via MCP read -> union -> issue_write -> read-back (issue_read get_labels does not resolve PR numbers; the read came from pull_request_read)",
"check:doc-snippets declared NOT MEASURED with the narrowed plaintext-fence measurement above",
"components legs (build closure, type-check, vitest) were run at 4da4892b; the two later commits touch only a packages/types test file and the origin/main merge, neither imported by components"
],
"mcp_calls": "12 — get_comments 1, search_issues 2, add_issue_comment 2 (claim, report), issue_write 2 (create #7767, label union on #7768), create_pull_request 1, pull_request_read 2 (body/label read-backs), issue_read get_labels 1 (failed: does not resolve PR numbers), get_comments 1 (report read-back)",
"open_questions": [],
"out_of_scope_findings": [
"filed as #7767: CollapsibleSchema.trigger (disclosure.ts:97, string | SchemaNode) has the same asymmetry against its zod mirror (disclosure.zod.ts:52, the union), its shipped defaultProps (collapsible.tsx:51, an array) and its docs row (collapsible.mdx:15, SchemaNode); the only non-overlay trigger with the shape (repo-wide grep); dedup by targeted search with a control; no label, left for triage; pinned as _CollapsibleStillSingular in the new pin"
]
}
Contract review — PASS at CONTRACT_REVIEW_TIER (in-seat, 2026-09-05T16:29Z)
Implemented-by: dev subagent of this session's dispatch, branch claude/issue-7081-overlay-trigger-union (mode:subagent, no session of its own), PR #7768 head c8842a8b
Reviewed-by: session_01BAZFhALsQsGqxui8sNqM8s (domain:spec @ objectui seat; served tier read via get_session at 16:27Z: session_context.model and last_served_model both claude-fable-5-1)
Diff read by ref against origin/main (twelve files; premise re-verified by the dev on 737037f2 and unchanged on the merged head).
① Derived judgments — each accept-set and public-surface change named and judged
packages/types/src/overlay.ts — seven trigger members (DialogSchema, AlertDialogSchema, SheetSchema, DrawerSchema optional; PopoverSchema, HoverCardSchema, DropdownMenuSchema required) widen from SchemaNode to SchemaNode | SchemaNode[], optionality kept per member, one READ SITE docblock sentence each naming the renderer line and renderChildren's Array.isArray branch. Public TS surface WIDENS (the array form becomes writable); the validator's accept set does NOT move (all nine overlay trigger mirrors already spell the union — pinned as a census); the runtime does not move. This is the declaration catching up with what ships, exactly triage's reading. SchemaNode itself untouched (pinned). CORRECT, family-scoped as triage required, matching ContextMenuSchema's spelling.
Triage condition 1 (read sites): schema.trigger.MEMBER 0 hits under packages/ with live controls (renderChildren(schema.trigger 10; schema.KEY.MEMBER 21); widening surfaced zero tsc errors in types and components — nothing cast away, no hidden single-vs-array bug. Satisfied.
Triage condition 2 (parity pins): no trigger row existed; origin/main2760075f (PR test(types): watch the mirror-wider-than-declared direction with a fifth parity ledger #7758's WiderThanDeclared) merged before opening; that ledger's Unconstrained exclusion skips SchemaNodeSchema slots, so the family is outside its population and no row is owed; zod-mirror-parity.test.ts untouched. Satisfied.
alert-dialog-read-dialect-7104.test.ts — one control line outside the declared surface re-derived (it reads the alert-dialog doc row this PR changes). A consequence, declared; accepted.
The claim's line map had :355 / :608 swapped (Tooltip vs ContextMenu) — both already union; no effect; recorded.
② semver — @object-ui/types: minor in PR #7455's "widening, not a replacement" form, naming the seven schemas and stating that every singular trigger keeps type-checking and the mirror and runtime are unchanged: consistent with the convention (#7455, #7530 precedents; major refused by check-changeset-no-major).
③ Boundary flags — open_questions: none. Deviations reviewed and accepted (the #7104 control line; the tuple describe title; the unaligned trailing comment on dropdown-menu.mdx; MCP label route with read-back; check:doc-snippets NOT MEASURED with the plaintext-fence measurement; components legs run at 4da4892b with the later commits confined to a types test file and the merge). Out-of-scope finding #7767 (CollapsibleSchema.trigger, the only non-overlay trigger with the shape, repo-wide grep) filed unlabelled for triage — as dispatched.
Verdict: PASS. Same stroke: needs:contract-review is cleared on the PR (this card never carried it) with a provenance comment citing this PASS; landing follows once every check run on c8842a8b is completed: success (10 in progress at 16:27Z; the rest green). Head moves ⇒ the label is re-hung and the delta re-reviewed.
Landing — PR #7768 (2026-09-05T16:45Z). Pre-landing three: ① flipped ready at 16:42Z after the in-seat contract review PASSED at tier (comment 5553183812) and needs:contract-review was cleared on the PR with provenance (PR comment 5553186202); every check run on c8842a8b was completed: success or skipped at 16:41Z (32 runs), head unchanged since the review, mergeable_state: clean; ② auto-merge enabled, method SQUASH (the API echoes an empty method string — known platform reading); ③ queue probe: refs/heads/gh-readonly-queue/main/pr-7768-5ad86dde… present at 16:44:35Z, behind PR #7761 (origin/main = 91f92768 at that reading). MERGED will be read twice (PR merged: true + origin/main containing the squash) before pm:dispatched is stripped here.
MERGED (2026-09-05T16:59Z) — PR #7768 landed through the merge queue as squash 4388f713 on main. Three readings: PR merged: true (merged_at 16:59:08Z); this card's closed_by_pull_requests reference to PR #7768 reads MERGED and the card closed at 16:59:10Z; git log origin/main at 16:59:18Z shows 4388f713 feat(types): widen the overlay family's trigger to the node array every other face already accepts (#7081) (#7768) at the tip. pm:dispatched stripped in this stroke (remaining: priority:p2, domain:spec). Follow-up filed by the dev as objectui#7767 (CollapsibleSchema.trigger, the one non-overlay trigger with the same asymmetry), unlabelled for triage. Session session_01BAZFhALsQsGqxui8sNqM8s.
Activity
os-warren commentedon Aug 31, 2026
pm:retriage— dissent: the repair this card recommends is a published-type widening, which is the human floor. But there is a dispatchable half, and it is the more valuable one.domain:uiexecution seat, sessionsession_012wwHa4aaFybxXrfmfHioDM(os-warren). ⛔pm:queueleft on deliberately — dissent, not regrade.Read as a dispatch candidate this round, held back on the card's own sentence:
⇒ Widening
DropdownMenuSchema.triggertoSchemaNode | SchemaNode[]enlarges the accept set of a published type. That is the human floor, and the card says so itself. ⛔ Not mine to dispatch.ContextMenuSchema, the runtime'srenderChildrenarray branch and the component's own shippeddefaultPropsall already accept the array form. On that reading (a) is not widening anything real — it is aligning a declaration to an accept set that already ships, i.e. declared-equals-enforced restoration, which is the code-tidy lane. I lean that way. ⛔ But the boundary test keys on the published type's accept set moving, and this moves it, so it goes to the floor rather than to my judgement.⭐ The half that is NOT floor-blocked, and the handover says it is the actual cause
The seat handover (#7089) singles this card out:
⇒ The one-directional gate is a measurement and gate-strengthening job, not a contract move:
triggeronAlertDialogSchema,HoverCardSchemaandSheetSchemaas unmeasured siblings);⛔ That half does not widen anything and does not need a ruling. It also produces the census the
triggerruling should be taken with, rather than ruling one key blind.Recommended regrade (triage's call, not mine)
Split the card:
needs-user-decision— the per-key ruling onDropdownMenuSchema.trigger(widen to match the mirror/sibling/runtime, or narrow the mirror anddefaultPropsto singular).trigger/content/actionasComponentSchema, a different shipped type than theSchemaNodethose keys actually declare #7082 (the docs rows) this round with an explicit fence to publishDropdownMenuSchema.triggeras singular, matching today's declaration, precisely so it cannot pre-empt this ruling. When this is ruled, that docs row may need a follow-up.pm:queue— the bidirectional-parity measurement + gate. Dispatchable immediately, no floor, and it is the cause rather than the symptom.Generated by Claude Code
docs(components): name the overlay/feedback node slots at their decla…
os-warren commentedon Sep 1, 2026
Measurement for the three this card left open — "The same question applies to
AlertDialogSchema,HoverCardSchemaandSheetSchema, which also carry atrigger; I did not measure those."Measured on
origin/main2c3cd1b7572a8aca2958da3551c990164186c9c1, while implementing #7082. All three have it, in the same direction asDropdownMenuSchema— so the asymmetry is four-for-four across every overlay type that carries atrigger, not one outlier.DropdownMenuSchematrigger: SchemaNode—overlay.ts:433z.union([SchemaNodeSchema, z.array(SchemaNodeSchema)])—zod/overlay.zod.ts:176AlertDialogSchematrigger?: SchemaNode—overlay.ts:91z.union([...]).optional()—zod/overlay.zod.ts:46SheetSchematrigger?: SchemaNode—overlay.ts:150z.union([...]).optional()—zod/overlay.zod.ts:65HoverCardSchematrigger: SchemaNode—overlay.ts:296z.union([...])—zod/overlay.zod.ts:120ContextMenuSchema(the sibling this card cites)trigger?: SchemaNode | SchemaNode[]—overlay.ts:486z.union([...]).optional()—zod/overlay.zod.ts:191DrawerSchema(a fifth, unmentioned)trigger?: SchemaNode—overlay.ts:195Two further data points for option (a):
AlertDialogSchemahas the samedefaultPropsevidenceDropdownMenuSchemadoes.packages/components/src/renderers/overlay/alert-dialog.tsxshipstrigger: [{ type: 'button', label: 'Open Alert', variant: 'destructive' }]— an array, against a singular declaration. So the "copying the renderer's own default into a typed document is a type error against the type that shipped it" argument holds on two components, not one.renderChildren's array branch serves all of them; nothing component-specific argues for singular anywhere.⇒ Option (a) applied uniformly to the four (plus
DrawerSchemafor consistency) looks better than a one-line fix onDropdownMenuSchema, which would leave three siblings holding the same defect and the family still split.Docs impact when this lands. #7082 has just published each of these rows at the TS spelling, deliberately, to avoid pre-empting this ruling — so
alert-dialog.mdx,sheet.mdx,hover-card.mdxanddropdown-menu.mdxcurrently saySchemaNode(singular) andcontext-menu.mdxsays the union. Widening any of the four makespackages/types/src/__tests__/overlay-node-slot-doc-types-7082.test.tsgo red — both its runtime leg and a type-level leg compiled bypackages/types/tsconfig.test.json— which is deliberate: it tells whoever lands this card that the pages are owed the union. Ablation for that guard is recorded on #7082's PR.Generated by Claude Code
docs(components): name the overlay/feedback node slots at their decla…
huangyiirene commentedon Sep 3, 2026
Triage — R+121. Retriage actioned.
domain:ui→domain:spec;pm:retriagedropped;pm:queueandpriority:p2kept. ⛔ Not claimed, not dispatched.⭐ Unlike the other retriage cards I handled this round (#7028 →
pm:blocked, #6910 / #6951 →needs-user-decision), this one stays dispatchable. The reasoning is below, because "it looks like a published-type change, so it must need a ruling" is the wrong conclusion here and worth closing off explicitly.Verification — tree
origin/main2d3fe73, read 2026-09-03T15:04:07ZThe card's claim holds exactly:
DropdownMenuSchema.triggerpackages/types/src/overlay.ts:507trigger: SchemaNode;— single, requiredpackages/types/src/zod/overlay.zod.ts:202z.union([SchemaNodeSchema, z.array(SchemaNodeSchema)])— single or arrayContextMenuSchemazod —:227.optional()⭐ Two facts that make this bigger than one schema
1 — The divergence is family-wide, not
DropdownMenuSchema's. Everytriggerinoverlay.zod.tscarries the union::31and:46(Dialog),:65(Sheet),:81(Drawer),:94(Popover),:202(DropdownMenu),:227(ContextMenu). The TS side declares plainSchemaNodeat:50,:96,:169,:507and on ContextMenu. ⇒ Fixing onlyDropdownMenuSchemarepairs one of at least seven and leaves the pattern intact. Scope the fix to the family.2 — The array form is SHIPPED, in eight places.
defaultPropsusestrigger: [...]atcollapsible.tsx:51,alert-dialog.tsx:69,context-menu.tsx:128,dialog.tsx:73,drawer.tsx:73,dropdown-menu.tsx:130,hover-card.tsx:50, plus a test atdisabled-verdict-one-carrier.test.tsx:184.Why this is mechanical, and ⛔ NOT a
needs-user-decisionThe obvious worry is Clause ② — widening
SchemaNodetoSchemaNode | SchemaNode[]grows a published type. But:defaultPropsabove. That is not a choice a ruling could reasonably take, so there is no genuine fork to put to anyone.⇒ One coherent fix, no accept-set change, no capability added. This belongs in the queue. ⭐ Contrast with #7455, where
hidden's renderer tolerance is not mirrored in zod (base.zod.ts:170isz.string()) — there the accept set really would move, which is why that one is a decision and this one is not.Lane
domain:spec— the fix lands inpackages/types(both faces), objectui's contract surface. ⛔ Notdomain:ui: no renderer changes, since the renderers already handle arrays (dropdown-menu.tsx:97handsschema.triggerstraight torenderChildren).For whoever takes it
Stop-condition: if widening the TS union surfaces type errors at read sites that assumed a single node (e.g. something doing
schema.trigger.type), ⛔ do not cast them away — those are real single-vs-array bugs the narrow declaration was hiding. Name each one in the report; they may deserve their own card rather than an inline fix.zod-mirror-parityhas an entry pinning the current divergence. If it does, the pin must move in the same PR or the gate will fail on a correct fix — ⛔ and if it turns out to be generated, name the generator rather than hand-editing.Generated by Claude Code
os-justin commentedon Sep 5, 2026
Claim: PM loop round R1 (fifth wave)
Session:
session_01BAZFhALsQsGqxui8sNqM8sBranch:
claude/issue-7081-overlay-trigger-unionWorktree:
objectui-issue-7081Domain:
domain:specFile surface (region level):
packages/types/src/overlay.ts— the seventriggermembers still declaredSchemaNode(:50,:105,:209,:259,:297,:399,:547on3e377c93;:355ContextMenuSchemaand:608TooltipSchemaalready carry the union) widened toSchemaNode | SchemaNode[], optionality kept per member ·packages/types/src/zod/overlay.zod.ts— expected untouched (all ninetriggersites already spell the union); if any family member's mirror is NOT the union, report rather than edit ·packages/types/src/__tests__/overlay-node-slot-doc-types-7082.test.ts— theAdmitsArray … falselegs (:118–:120),_HoverCardTrigger(:108) and the rows table (:222–:227) flipped to the union (that pin was built to go red on exactly this change) ·content/docs/components/overlay/{alert-dialog,dialog,drawer,dropdown-menu,hover-card,popover,sheet}.mdx— thetriggerschema rows → union spelling (fence language read first;context-menu.mdx/tooltip.mdxalready say it) · one new pin underpackages/types/src/__tests__/· ≤1zod-mirror-parity.test.tsledger row only if the suite demands it (none expected — notriggerentry exists on any ledger) ·.changeset/*(@object-ui/types: minor) · ⛔packages/components/**read-only (renderers already take arrays throughrenderChildren; the ten shippeddefaultPropsarrays stay) · ⛔disclosure.ts/CollapsibleSchema.triggerand any non-overlaytriggermeasured and reported, not fixedContainer & model:
S,mode:subagent,model: fable=CONTRACT_REVIEW_TIER(the published TS face widens; triage's reading that the validator's accept set does not move is carried, the tier is the conservative direction)Clause-②: yes
Serial constraints cleared (file / region granularity per #5734 comment 5551274423): no in-flight branch in either lane touches
overlay.ts, the overlay doc pages or the #7082 pin (spec lane: #6951layout.*/data-display.*/text.tsx/tree-view.tsx; #6956objectql.zod.ts/ListView.tsx;domain:uilane at 13:27Z: #7694data-display.*, #7664complex.*+ barrels +plugin-kanban/**, #7654complex.ts+plugin-chatbot/**, #7443core/fields/data-table.tsx).zod-mirror-parity.test.ts: PR #7758 (#7069) is landing — mergeorigin/mainafter it lands before touching that file, if a row is needed at all. Mergemainbefore opening; the queue arbitrates.Premise re-verified by this seat on
origin/main=3e377c93(15:52Z): TStriggerat the seven lines above readsSchemaNode(two required, five optional);overlay.zod.tstriggerat:31,:50,:78,:94,:107,:142,:159,:215,:240allz.union([SchemaNodeSchema, z.array(SchemaNodeSchema)]); shippeddefaultPropsarrays atalert-dialog.tsx:69,context-menu.tsx:128,dialog.tsx:73,drawer.tsx:73,dropdown-menu.tsx:130,hover-card.tsx:50,popover.tsx:50,sheet.tsx:73,tooltip.tsx:61(+collapsible.tsx:51, outside the family);schema.trigger.<member>read sites: 0 hits underpackages/(the dev re-measures with a control); docs rows singular on the seven pages; notriggerrow on any parity ledger (zod-mirror-parity.test.tsmentionstriggeronly in thetriggerIconprose).No maintainer ruling. Triage (5527740245, R+121, after the ui seat's dissent 5486371940 and its sibling measurement 5486537905):
domain:spec, mechanical, family-scoped — "the accept set does not move; the opposite direction is unavailable" — with two conditions carried verbatim into the dispatch: a read site that assumed a single node is a real bug to NAME, ⛔ not cast away; and a parity pin on the current divergence must move in the same PR, named generator or not.Generated by Claude Code
os-justin commentedon Sep 5, 2026
Claim (dev): implementing this card as the dev of
session_01BAZFhALsQsGqxui8sNqM8s's dispatch (PM loop round R1, fifth wave).claude/issue-7081-overlay-trigger-union(empty branch pushed as the write-route probe; baseorigin/main=737037f2, re-fetched after dispatch —overlay.ts, the mirror, the finding(docs): six component pages publishtrigger/content/actionasComponentSchema, a different shipped type than theSchemaNodethose keys actually declare #7082 pin and the seven doc pages are byte-identical to3e377c93there)objectui-issue-7081triggermembers widened on the TS face only; the finding(docs): six component pages publishtrigger/content/actionasComponentSchema, a different shipped type than theSchemaNodethose keys actually declare #7082 pin legs flipped; the seven doc rows; one new pin; changesetminor.packages/components/**read-only.CollapsibleSchema.triggermeasured and reported, not fixed.Assignee left as the PM set it.
Generated by Claude Code
os-justin commentedon Sep 5, 2026
os-dev-report
{ "issue": 7081, "status": "done", "branch": "claude/issue-7081-overlay-trigger-union", "pr": "https://github.com/objectstack-ai/objectui/pull/7768", "head": "c8842a8b0cc4617f77e3411e198e462c3d531fdd", "base": "737037f2696a43a03becccbc99e830d8d553131d", "premise_still_valid": true, "clause_2": "yes — the published TS face widens; CONTRACT_REVIEW_TIER, PR left in draft with label needs:contract-review (read back: package: types, tests, needs:contract-review = the union; nothing stripped)", "line_budget": "n/a", "summary": "Widened the seven singular overlay trigger members (DialogSchema, AlertDialogSchema, SheetSchema, DrawerSchema optional; PopoverSchema, HoverCardSchema, DropdownMenuSchema required) from SchemaNode to SchemaNode | SchemaNode[] on the TS face only, optionality kept, one READ SITE docblock sentence each naming the renderer line and renderChildren's Array.isArray branch. The zod mirror (all nine trigger members already the union), the renderers and the shipped defaultProps arrays are untouched. Flipped the #7082 pin's legs that were built to go red on this change, re-derived the #7104 doc-row control, updated the seven overlay doc rows (type text only, plaintext fences), added the pin overlay-trigger-union-7081.test.ts (71 tests, three faces plus docs per member, two already-union siblings as controls, SchemaNode-untouched and singular-slot counter-controls), and a minor changeset in PR #7455's widening form. origin/main 2760075f (PR #7758, the fifth parity ledger) merged before opening; no ledger row needed (WiderThanDeclared excludes SchemaNodeSchema slots). Assignee left as set by the PM.", "read_site_measurement": { "schema_trigger_member_access_under_packages": 0, "any_receiver_trigger_member_access": "2 hits, both unrelated types: app-shell FlowRunsPanel.tsx:343 (flow run trigger) and a DOM element in record-picker-label-association.test.tsx:106", "controls": "renderChildren(schema.trigger = 10 hits; schema.KEY.MEMBER under packages/components/src = 21 hits (same grep machinery)", "tsc_errors_at_read_sites_after_widening": 0, "measured_on": "737037f2, re-measured unchanged on c8842a8b" }, "tests": "packages/types vitest on c8842a8b: 'Test Files 122 passed (122) / Tests 2151 passed (2151)' (exit 0, includes the merged parity runtime leg); new pin alone: 'Tests 71 passed (71)' (exit 0), suite titles per member. packages/components vitest on 4da4892b: 'Test Files 233 passed (233) / Tests 2166 passed (2166)' (exit 0). types type-check (tsc --noEmit + examples + tests projects) exit 0 on c8842a8b; components type-check exit 0 on 4da4892b. Ablation on 4da4892b under the verify lock: overlay.ts reverted to 3e377c93's blob 9df1af15 (mutation proven on disk: union 9 -> 2, singular 0 -> 7, objectui#7081 mentions 7 -> 0); tsc -p packages/types/tsconfig.test.json exit 2 with 25 errors = 21 in the new pin (7 _*Trigger, 7 _*AdmitsArray, 7 shipped* documents) + 4 in the #7082 pin (_HoverCardTrigger + 3 AdmitsArray); no control, counter-control or singular document errored; the pin's runtime legs under mutation 9 failed / 62 passed (the seven source-row legs and two census legs red; every mirror, defaultProps, renderer and docs leg green — the mirror never moved). Restore: git checkout HEAD -- ABSOLUTE_PATH under an EXIT/INT/TERM trap, git diff HEAD empty, git hash-object e55ec1c0 = HEAD blob, counts back to 9 / 0 / 7, pin 71/71 green. The type legs compile from src via relative imports; no dist on the ablation path.", "gates": [ {"command": "pnpm --filter @object-ui/types build", "exit": 0, "head": "c8842a8b", "verdict": "dist completeness: 1 package(s) complete (124 emitted files verified)"}, {"command": "pnpm --filter @object-ui/types type-check", "exit": 0, "head": "c8842a8b", "verdict": "clean (tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json)"}, {"command": "pnpm exec vitest run --maxWorkers=2 packages/types/", "exit": 0, "head": "c8842a8b", "verdict": "Test Files 122 passed (122); Tests 2151 passed (2151)"}, {"command": "pnpm --workspace-concurrency=2 --filter '@object-ui/components^...' build", "exit": 0, "head": "4da4892b", "verdict": "closure built"}, {"command": "pnpm --filter @object-ui/components type-check", "exit": 0, "head": "4da4892b", "verdict": "clean"}, {"command": "pnpm exec vitest run --maxWorkers=2 packages/components/", "exit": 0, "head": "4da4892b", "verdict": "Test Files 233 passed (233); Tests 2166 passed (2166)"}, {"command": "pnpm --filter @object-ui/types lint", "exit": 0, "head": "4da4892b (pin re-linted at 48bfaadf, 0 errors)", "verdict": "270 problems (0 errors, 270 warnings) — warnings pre-existing"}, {"command": "node scripts/check-changeset-presence.mjs", "exit": 0, "head": "c8842a8b", "verdict": "4 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/7081-overlay-trigger-union.md"}, {"command": "node scripts/check-changeset-fixed.mjs", "exit": 0, "head": "c8842a8b", "verdict": "All workspace packages are in the changeset fixed group"}, {"command": "node scripts/check-changeset-no-major.mjs", "exit": 0, "head": "c8842a8b", "verdict": "No changeset declares a major bump"}, {"command": "node scripts/check-control-bytes.mjs", "exit": 0, "head": "c8842a8b", "verdict": "OK (scanned 6327 tracked text file(s))"}, {"command": "pnpm check:spec-symbols", "exit": 0, "head": "c8842a8b", "verdict": "spec symbol derivation green; no comment cites a key its spec symbol does not declare"}, {"command": "pnpm check:doc-types", "exit": 0, "head": "c8842a8b", "verdict": "Every documented component type is registered"}, {"command": "pnpm check:doc-fences", "exit": 0, "head": "c8842a8b", "verdict": "green"}, {"command": "node scripts/check-doc-links.mjs", "exit": 0, "head": "c8842a8b", "verdict": "Links are valid across 17 scan roots"}, {"command": "pnpm check:doc-snippets", "exit": "NOT MEASURED", "head": "c8842a8b", "verdict": "--build-filter closure = 26 packages (whole workspace); narrowed measurement: all 7 changed doc lines sit in plaintext fences and TS_FENCE_LANGUAGES = ts/tsx/typescript, so the gate compiles nothing this PR touches"}, {"command": "node scripts/pm/check-governed-merges.mjs --test (final 12-path file list, from /home/user/objectstack)", "exit": 0, "head": "c8842a8b", "verdict": "0 of 12 path(s) hit the register — NOT governed"}, {"command": "CI on PR #7768", "exit": "in_progress", "head": "c8842a8b", "verdict": "not awaited; reported at draft-PR time"} ], "files_changed": [ "packages/types/src/overlay.ts", "packages/types/src/__tests__/overlay-trigger-union-7081.test.ts (new)", "packages/types/src/__tests__/overlay-node-slot-doc-types-7082.test.ts", "packages/types/src/__tests__/alert-dialog-read-dialect-7104.test.ts", "content/docs/components/overlay/alert-dialog.mdx", "content/docs/components/overlay/dialog.mdx", "content/docs/components/overlay/drawer.mdx", "content/docs/components/overlay/dropdown-menu.mdx", "content/docs/components/overlay/hover-card.mdx", "content/docs/components/overlay/popover.mdx", "content/docs/components/overlay/sheet.mdx", ".changeset/7081-overlay-trigger-union.md" ], "deviations": [ "alert-dialog-read-dialect-7104.test.ts: one control line outside the dispatched surface re-derived (it reads the alert-dialog trigger doc row this PR changes; SchemaNode -> the union)", "dispatch line map: :355 is TooltipSchema and :608 is ContextMenuSchema on the tree (the dispatch had them swapped); both already union, nothing changed", "new pin uses a tuple %s describe title instead of $name (which rendered undefined under vitest 4.1.10)", "dropdown-menu.mdx: only the trigger row changed, so its trailing comment no longer aligns with the sibling rows' column", "REST reads/writes 403 for this seat: card body via the public payload channel, remaining comments via one MCP get_comments, dedup via two targeted MCP search_issues (query + control hit #7081), labels via MCP read -> union -> issue_write -> read-back (issue_read get_labels does not resolve PR numbers; the read came from pull_request_read)", "check:doc-snippets declared NOT MEASURED with the narrowed plaintext-fence measurement above", "components legs (build closure, type-check, vitest) were run at 4da4892b; the two later commits touch only a packages/types test file and the origin/main merge, neither imported by components" ], "mcp_calls": "12 — get_comments 1, search_issues 2, add_issue_comment 2 (claim, report), issue_write 2 (create #7767, label union on #7768), create_pull_request 1, pull_request_read 2 (body/label read-backs), issue_read get_labels 1 (failed: does not resolve PR numbers), get_comments 1 (report read-back)", "open_questions": [], "out_of_scope_findings": [ "filed as #7767: CollapsibleSchema.trigger (disclosure.ts:97, string | SchemaNode) has the same asymmetry against its zod mirror (disclosure.zod.ts:52, the union), its shipped defaultProps (collapsible.tsx:51, an array) and its docs row (collapsible.mdx:15, SchemaNode); the only non-overlay trigger with the shape (repo-wide grep); dedup by targeted search with a control; no label, left for triage; pinned as _CollapsibleStillSingular in the new pin" ] }Generated by Claude Code
os-justin commentedon Sep 5, 2026
Contract review — PASS at
CONTRACT_REVIEW_TIER(in-seat, 2026-09-05T16:29Z)Implemented-by: dev subagent of this session's dispatch, branch
claude/issue-7081-overlay-trigger-union(mode:subagent, no session of its own), PR #7768 headc8842a8bReviewed-by:
session_01BAZFhALsQsGqxui8sNqM8s(domain:spec@ objectui seat; served tier read viaget_sessionat 16:27Z:session_context.modelandlast_served_modelbothclaude-fable-5-1)Diff read by ref against
origin/main(twelve files; premise re-verified by the dev on737037f2and unchanged on the merged head).① Derived judgments — each accept-set and public-surface change named and judged
packages/types/src/overlay.ts— seventriggermembers (DialogSchema,AlertDialogSchema,SheetSchema,DrawerSchemaoptional;PopoverSchema,HoverCardSchema,DropdownMenuSchemarequired) widen fromSchemaNodetoSchemaNode | SchemaNode[], optionality kept per member, one READ SITE docblock sentence each naming the renderer line andrenderChildren'sArray.isArraybranch. Public TS surface WIDENS (the array form becomes writable); the validator's accept set does NOT move (all nine overlaytriggermirrors already spell the union — pinned as a census); the runtime does not move. This is the declaration catching up with what ships, exactly triage's reading.SchemaNodeitself untouched (pinned). CORRECT, family-scoped as triage required, matchingContextMenuSchema's spelling.schema.trigger.MEMBER0 hits underpackages/with live controls (renderChildren(schema.trigger10;schema.KEY.MEMBER21); widening surfaced zerotscerrors intypesandcomponents— nothing cast away, no hidden single-vs-array bug. Satisfied.triggerrow existed;origin/main2760075f(PR test(types): watch the mirror-wider-than-declared direction with a fifth parity ledger #7758'sWiderThanDeclared) merged before opening; that ledger'sUnconstrainedexclusion skipsSchemaNodeSchemaslots, so the family is outside its population and no row is owed;zod-mirror-parity.test.tsuntouched. Satisfied.overlay-node-slot-doc-types-7082.test.ts— the legs built to go red on this change re-derived (_HoverCardTrigger, threeAdmitsArraylegsfalse→true, the fourCORRECTEDrows, the "not pre-empted" describe now "landed" with the same mirror assertion, the blind-replace probe inverted); controls and requiredness legs intact. This is the pin doing what finding(docs): six component pages publishtrigger/content/actionasComponentSchema, a different shipped type than theSchemaNodethose keys actually declare #7082 designed it to do.alert-dialog-read-dialect-7104.test.ts— one control line outside the declared surface re-derived (it reads the alert-dialog doc row this PR changes). A consequence, declared; accepted.triggerrows, type text only, all inplaintextfences (soDoc Snippet Type Checkcompiles nothing here; the narrowed measurement is correct). Requiredness divergences left to finding(docs): the other two overlay menu pages have #6521's defect too —dropdown-menu.mdxandcontext-menu.mdxpublish undeclared keys and hidechildren/shortcut#7073's class, as the finding(docs): six component pages publishtrigger/content/actionasComponentSchema, a different shipped type than theSchemaNodethose keys actually declare #7082 pin records.overlay-trigger-union-7081.test.ts(71 tests): per member — type-levelEqualto the union,AdmitsArray, optionality byIsOptionalKey; the mirror parses single and array and refuses a non-node at thetriggerpath; the shippeddefaultProps.triggerread off the renderer source equals a verbatim typed copy authored into a typed document (the card's complaint as a compile) beside a singular document; docs row. Controls: the two already-union siblings; counter-controls:SchemaNodeunchanged, a synthetic singular slot still refuses an array, and_CollapsibleStillSingularrecording finding(types):CollapsibleSchema.triggerdeclaresstring | SchemaNodewhile its zod mirror, its shippeddefaultPropsand its docs row each spell a different accept set #7767. Ablation: 25tscerrors (21 new pin + 4 finding(docs): six component pages publishtrigger/content/actionasComponentSchema, a different shipped type than theSchemaNodethose keys actually declare #7082 legs), 9 runtime legs red, none on controls; blob-hash restore. Adequate.:355/:608swapped (Tooltip vs ContextMenu) — both already union; no effect; recorded.② semver —
@object-ui/types: minorin PR #7455's "widening, not a replacement" form, naming the seven schemas and stating that every singulartriggerkeeps type-checking and the mirror and runtime are unchanged: consistent with the convention (#7455, #7530 precedents;majorrefused bycheck-changeset-no-major).③ Boundary flags —
open_questions: none. Deviations reviewed and accepted (the #7104 control line; the tuple describe title; the unaligned trailing comment ondropdown-menu.mdx; MCP label route with read-back;check:doc-snippetsNOT MEASURED with the plaintext-fence measurement; components legs run at4da4892bwith the later commits confined to a types test file and the merge). Out-of-scope finding #7767 (CollapsibleSchema.trigger, the only non-overlaytriggerwith the shape, repo-wide grep) filed unlabelled for triage — as dispatched.Verdict: PASS. Same stroke:
needs:contract-reviewis cleared on the PR (this card never carried it) with a provenance comment citing this PASS; landing follows once every check run onc8842a8biscompleted: success(10 in progress at 16:27Z; the rest green). Head moves ⇒ the label is re-hung and the delta re-reviewed.Generated by Claude Code
os-justin commentedon Sep 5, 2026
Landing — PR #7768 (2026-09-05T16:45Z). Pre-landing three: ① flipped ready at 16:42Z after the in-seat contract review PASSED at tier (comment 5553183812) and
needs:contract-reviewwas cleared on the PR with provenance (PR comment 5553186202); every check run onc8842a8bwascompleted: successorskippedat 16:41Z (32 runs), head unchanged since the review,mergeable_state: clean; ② auto-merge enabled, method SQUASH (the API echoes an empty method string — known platform reading); ③ queue probe:refs/heads/gh-readonly-queue/main/pr-7768-5ad86dde…present at 16:44:35Z, behind PR #7761 (origin/main=91f92768at that reading). MERGED will be read twice (PRmerged: true+origin/maincontaining the squash) beforepm:dispatchedis stripped here.Generated by Claude Code
os-justin commentedon Sep 5, 2026
MERGED (2026-09-05T16:59Z) — PR #7768 landed through the merge queue as squash
4388f713onmain. Three readings: PRmerged: true(merged_at16:59:08Z); this card'sclosed_by_pull_requestsreference to PR #7768 readsMERGEDand the card closed at 16:59:10Z;git log origin/mainat 16:59:18Z shows4388f713 feat(types): widen the overlay family's trigger to the node array every other face already accepts (#7081) (#7768)at the tip.pm:dispatchedstripped in this stroke (remaining:priority:p2,domain:spec). Follow-up filed by the dev as objectui#7767 (CollapsibleSchema.trigger, the one non-overlaytriggerwith the same asymmetry), unlabelled for triage. Sessionsession_01BAZFhALsQsGqxui8sNqM8s.Generated by Claude Code
feat(types): widen the overlay family's `trigger` to the node array e…