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
Decision: registry-inputs-spec-parity fails on the 17.3.0 merge — a calibration control now asserts something the contract accepts, and the kind classifier may not recurse through z.union #8137
Filed by the domain:spec @ objectui execution seat (session session_0114Ytxr5sM1vdW19Y9WAx6E), R1. Blocks PR #7685 (#7122's 17.3.0 pin). ⛔ Not decided here — one half sits on the manual floor.
What fails
apps/console/src/__tests__/registry-inputs-spec-parity.test.ts, 2 failed / 179 passed, deterministic on a targeted re-run.
⚠️Merge-only, and not a resolution anyone could have made differently. Green on main (spec 17.2.0), green on the branch (the gate did not exist there), red only in the merge. main's cf1d29e2f (#8097, +1077 lines) added the ComponentInput.of gate; it auto-merged textually with this branch because the two sides edited disjoint regions.
Root cause — measured on the installed artifact, not inferred
@objectstack/spec 17.3.0 changed RecordActivityProps.types to an open vocabulary:
That is objectstack#11658 executing the maintainer's 2026-08-24 ruling on #11507 — "every closed map over this vocabulary is now the bug". safeParse against @objectstack/spec/ui:
The two failures are different in kind, and only one is a decision
1. A calibration control that reality overtook — ⛔ manual floor
The test asserts specMemberVerdict('record:activity', 'types', 'array', 'Account') === 'refuses-content'. 17.3.0 genuinely accepts'Account', so the assertion is now false. The test's own comment states the retired premise: "record:activity.types is a spec enum of strings."
⇒ The fix is to update a calibration control. That is the move the gate's own header forbids — and the protocol puts gate weakening on the manual floor precisely because an agent has a structural incentive to call weakening "correcting". ⭐ The distinction here is real but it is not mine to draw: the control is factually wrong, yet "the control is wrong" is also exactly what someone would say who wanted the gate quiet.
2. A probable classifier gap — likely an ordinary bug, reported for confirmation
record:activity.types:of=string fails the single-kind direction with the gate reporting contract accepts {string, array}. ⚠️ That reading disagrees with the contract as measured above: an array member is refused (invalid_union @ [0]).
Mechanism, from the dev that found it: acceptedMemberKinds counts a kind when the verdict is acceptsorrefuses-content, and refusesKind has to recurse through a z.union to answer correctly. If that is the whole story, this is a classifier that cannot see inside a union — not a genuinely multi-kind member — and the fix is in the gate's own helper, not in an exemption list.
⇒ Which it is decides the shape of the fix: a MULTI_KIND_MEMBER_CONTRACTS entry (declaring the member multi-kind) versus fixing refusesKind to recurse. The first would be wrong if the second is true, and it would also silently widen what the gate tolerates.
Four-axis analysis
Real business need — record:activity is a shipped block; the open vocabulary is the maintainer's own ruling being executed upstream. objectui has to follow it either way. The question is only whether the gate that guards this follows it correctly or is quieted.
Long-term soundness — ⭐ This is the axis that decides it. If (2) is a classifier gap, then every union-typed member in the contract is currently mis-classified by this gate, and an exemption entry would hide that for all of them, not just this one. Fixing the helper restores the gate's meaning; exempting one member spends the gate's credibility to make one PR green.
Preventing AI-written mistakes — the calibration control exists because an agent will otherwise adjust the thing being measured. Whatever is decided, the control should end up asserting something still true and still discriminating — ⛔ not deleted, and ⛔ not softened to "accepts anything".
Startup-stage focus — both fixes are small and neither adds surface. No expansion pressure here.
Options
A — Rule that updating the calibration control is a correction, not a weakening, since 17.3.0 genuinely accepts 'Account'; re-point it at a value the contract still refuses ([42] and [[]] both are), so it keeps discriminating. Then fix refusesKind to recurse through z.union and confirm the {string, array} reading collapses to {string}. Recommended. It keeps the control load-bearing and repairs the classifier rather than exempting around it.
B — Same control update, but declare record:activity.types in MULTI_KIND_MEMBER_CONTRACTS instead of touching the classifier. ⚠️ Cheaper now, but if the classifier really cannot see into unions this hides the same defect everywhere else it occurs.
#7122 / PR #7685 (the pin that surfaced it) · objectstack#11658 and objectstack#11507 (the open-vocabulary ruling) · #8097 / cf1d29e2f (the gate) · #8127 (a different 17.3.0 union-drift finding from the same round)
Filed by the
domain:spec@ objectui execution seat (sessionsession_0114Ytxr5sM1vdW19Y9WAx6E), R1. Blocks PR #7685 (#7122's 17.3.0 pin). ⛔ Not decided here — one half sits on the manual floor.What fails
apps/console/src/__tests__/registry-inputs-spec-parity.test.ts, 2 failed / 179 passed, deterministic on a targeted re-run.main(spec 17.2.0), green on the branch (the gate did not exist there), red only in the merge.main'scf1d29e2f(#8097, +1077 lines) added theComponentInput.ofgate; it auto-merged textually with this branch because the two sides edited disjoint regions.Root cause — measured on the installed artifact, not inferred
@objectstack/spec17.3.0 changedRecordActivityProps.typesto an open vocabulary:That is objectstack#11658 executing the maintainer's 2026-08-24 ruling on #11507 — "every closed map over this vocabulary is now the bug".
safeParseagainst@objectstack/spec/ui:The two failures are different in kind, and only one is a decision
1. A calibration control that reality overtook — ⛔ manual floor
The test asserts
specMemberVerdict('record:activity', 'types', 'array', 'Account') === 'refuses-content'. 17.3.0 genuinely accepts'Account', so the assertion is now false. The test's own comment states the retired premise: "record:activity.typesis a spec enum of strings."⇒ The fix is to update a calibration control. That is the move the gate's own header forbids — and the protocol puts gate weakening on the manual floor precisely because an agent has a structural incentive to call weakening "correcting". ⭐ The distinction here is real but it is not mine to draw: the control is factually wrong, yet "the control is wrong" is also exactly what someone would say who wanted the gate quiet.
2. A probable classifier gap — likely an ordinary bug, reported for confirmation
record:activity.types:of=stringfails the single-kind direction with the gate reportingcontract accepts {string, array}.invalid_union @ [0]).Mechanism, from the dev that found it:
acceptedMemberKindscounts a kind when the verdict isacceptsorrefuses-content, andrefusesKindhas to recurse through az.unionto answer correctly. If that is the whole story, this is a classifier that cannot see inside a union — not a genuinely multi-kind member — and the fix is in the gate's own helper, not in an exemption list.⇒ Which it is decides the shape of the fix: a
MULTI_KIND_MEMBER_CONTRACTSentry (declaring the member multi-kind) versus fixingrefusesKindto recurse. The first would be wrong if the second is true, and it would also silently widen what the gate tolerates.Four-axis analysis
Real business need —
record:activityis a shipped block; the open vocabulary is the maintainer's own ruling being executed upstream. objectui has to follow it either way. The question is only whether the gate that guards this follows it correctly or is quieted.Long-term soundness — ⭐ This is the axis that decides it. If (2) is a classifier gap, then every union-typed member in the contract is currently mis-classified by this gate, and an exemption entry would hide that for all of them, not just this one. Fixing the helper restores the gate's meaning; exempting one member spends the gate's credibility to make one PR green.
Preventing AI-written mistakes — the calibration control exists because an agent will otherwise adjust the thing being measured. Whatever is decided, the control should end up asserting something still true and still discriminating — ⛔ not deleted, and ⛔ not softened to "accepts anything".
Startup-stage focus — both fixes are small and neither adds surface. No expansion pressure here.
Options
'Account'; re-point it at a value the contract still refuses ([42]and[[]]both are), so it keeps discriminating. Then fixrefusesKindto recurse throughz.unionand confirm the{string, array}reading collapses to{string}. Recommended. It keeps the control load-bearing and repairs the classifier rather than exempting around it.record:activity.typesinMULTI_KIND_MEMBER_CONTRACTSinstead of touching the classifier.mainis shared damage.Related
#7122 / PR #7685 (the pin that surfaced it) · objectstack#11658 and objectstack#11507 (the open-vocabulary ruling) · #8097 /
cf1d29e2f(the gate) · #8127 (a different 17.3.0 union-drift finding from the same round)