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
Found while implementing #5955 (PR #6375, direction A: move the surface history sentence to the end of strictUnknownKeyError's message so the fix is read first). Unassigned — filed for triage, not claimed. Out of that PR's scope by construction: the ruling scoped it to one concatenation point in packages/spec/src/shared/suggestions.zod.ts, and none of these sites goes through it.
The finding
Three unrecognized_keys error maps are written by hand instead of via strictUnknownKeyError / strictObject, and each reproduces the exact shape #5955 was filed against — a non-actionable explanatory sentence sitting between the offending key and the prescription that fixes it:
"Undeclared top-level keys were dropped silently before strict validation, shipping inert metadata; a stale or mis-layered key is now a loud parse error." (~150 chars)
three separate branches: the ADR-0021 dataset migration, the objectui-internal component/data quarantine, and the #5022 drill near-key answer
"The two supported tenancy modes are: database-per-tenant = environment-level deployment (no object config); row-level isolation = tenancy.enabled + tenancy.tenantField."
the per-key \n • bullets built just above it
Every one is built as const base = front matter + sentence and then return base + ' ' + prescription, so the prescription is unreachable by a reader who stops at the front of a single-line render — which is the consumption position #5955 measured (os validate's • where: message at packages/cli/src/commands/validate.ts:141, CI logs, and validateFlowTriggerReadiness, which flattens newlines out of schema text on purpose).
Two sibling maps are fine and worth naming so a fixer does not touch them: packages/spec/src/ai/tool.zod.ts:85-93 and packages/spec/src/data/object.zod.ts:163-171 (enable) carry no explanatory sentence at all — front matter, then bullets.
So this is not "the same work, later" — it is a set of surfaces that both campaigns' inventories miss. The aliases/guidance integrity gate has the same blind spot for the same reason: alias-integrity.test.ts judges the two registries (strictObjectDeclarations() and directAliasTables()), and a hand-rolled map registers in neither, so its prescriptions are unmeasured rather than clean.
Candidate directions (for triage, not a recommendation to implement)
Fold them into strictObject / strictUnknownKeyError — the guidance channel already models exactly what all three do (visibility.ts's alias pointer, dashboard's three branches, tenancy's mode explainer are per-key prescriptions), so this would delete the copies and pull them under the integrity gate at the same time. Larger, and dashboard's branches are keyed by set membership (LEGACY_WIDGET_ANALYTICS_KEYS, QUARANTINED_WIDGET_KEYS) rather than by exact key, which guidance does not express today — that gap is the real decision.
Leave as-is and accept the divergence.
Direction 2 is the contract-first shape and would close the gate blind spot too, but it needs a call on whether guidance grows a set-keyed form; that is a spec-surface decision, not a mechanical one.
Verification notes
Read off origin/main at bbd2d8d3d. Duplicate search run over open issues on the file paths, on strictUnknownKeyError, and on the message-order wording — no hit. Related: #5955 (the ruling and the measurement this generalizes), #5762 (why the position started mattering), #5593 (the migration that does not cover these), #4001 / ADR-0078 (the strictness ratchet), #5013 / #5483 (the two registries the gate reads).
Found while implementing #5955 (PR #6375, direction A: move the surface
historysentence to the end ofstrictUnknownKeyError's message so the fix is read first). Unassigned — filed for triage, not claimed. Out of that PR's scope by construction: the ruling scoped it to one concatenation point inpackages/spec/src/shared/suggestions.zod.ts, and none of these sites goes through it.The finding
Three
unrecognized_keyserror maps are written by hand instead of viastrictUnknownKeyError/strictObject, and each reproduces the exact shape #5955 was filed against — a non-actionable explanatory sentence sitting between the offending key and the prescription that fixes it:packages/spec/src/shared/visibility.ts:94-112(strictVisibilityError)visibleWhen(ADR-0089) —visibleOn… andvisibility… are still accepted as deprecated aliases"packages/spec/src/ui/dashboard.zod.ts:145-190(strictWidgetAnalyticsError)component/dataquarantine, and the #5022 drill near-key answerpackages/spec/src/data/object.zod.ts:428-438(tenancy)tenancy.enabled+tenancy.tenantField."\n •bullets built just above itEvery one is built as
const base = front matter + sentenceand thenreturn base + ' ' + prescription, so the prescription is unreachable by a reader who stops at the front of a single-line render — which is the consumption position #5955 measured (os validate's• where: messageatpackages/cli/src/commands/validate.ts:141, CI logs, andvalidateFlowTriggerReadiness, which flattens newlines out of schema text on purpose).Two sibling maps are fine and worth naming so a fixer does not touch them:
packages/spec/src/ai/tool.zod.ts:85-93andpackages/spec/src/data/object.zod.ts:163-171(enable) carry no explanatory sentence at all — front matter, then bullets.Why neither in-flight change reaches them
historyto the end insidestrictUnknownKeyError. These three maps never call it, so they are untouched. After that PR lands, the shared surfaces and these three disagree about where the explanation goes.strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593 migrates the 44 directstrictUnknownKeyErrorcall sites tostrictObject. These three are not direct call sites either — they are independentz.core.$ZodErrorMapfunctions passed as{ error }. They are outside its completion scope, which is why this is filed standalone rather than as a sub-issue of 把 44 个strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593, and why there is noBlocked-by:line: the fix does not depend on that migration landing.So this is not "the same work, later" — it is a set of surfaces that both campaigns' inventories miss. The
aliases/guidanceintegrity gate has the same blind spot for the same reason:alias-integrity.test.tsjudges the two registries (strictObjectDeclarations()anddirectAliasTables()), and a hand-rolled map registers in neither, so its prescriptions are unmeasured rather than clean.Candidate directions (for triage, not a recommendation to implement)
strictObject/strictUnknownKeyError— theguidancechannel already models exactly what all three do (visibility.ts's alias pointer, dashboard's three branches,tenancy's mode explainer are per-key prescriptions), so this would delete the copies and pull them under the integrity gate at the same time. Larger, and dashboard's branches are keyed by set membership (LEGACY_WIDGET_ANALYTICS_KEYS,QUARANTINED_WIDGET_KEYS) rather than by exact key, whichguidancedoes not express today — that gap is the real decision.Direction 2 is the contract-first shape and would close the gate blind spot too, but it needs a call on whether
guidancegrows a set-keyed form; that is a spec-surface decision, not a mechanical one.Verification notes
Read off
origin/mainatbbd2d8d3d. Duplicate search run over open issues on the file paths, onstrictUnknownKeyError, and on the message-order wording — no hit. Related: #5955 (the ruling and the measurement this generalizes), #5762 (why the position started mattering), #5593 (the migration that does not cover these), #4001 / ADR-0078 (the strictness ratchet), #5013 / #5483 (the two registries the gate reads).