Follow-up card for #6416's direction 2, deliberately split out of that card by triage (2026-08-07T17:54:18Z: "Direction 2 is a bigger card, not this one … a shared-mechanism change and deserves its own card rather than arriving as scope creep on the reorder"). Direction 1 (the in-place reorders) landed as PR #6453 (bf0ae9976).
What this card does
Migrate the three hand-written $ZodErrorMaps to the shared strictObject / strictUnknownKeyError machinery, deleting the hand-maintained template copies:
packages/spec/src/shared/visibility.ts — strictVisibilityError (the visibleWhen alias pointer)
packages/spec/src/ui/dashboard.zod.ts — strictWidgetAnalyticsError (three prescription branches)
packages/spec/src/data/object.zod.ts — strictTenancyError (per-key tombstone bullets)
The real decision inside it (why it is not mechanical)
dashboard.zod.ts's branches are keyed by set membership (LEGACY_WIDGET_ANALYTICS_KEYS, QUARANTINED_WIDGET_KEYS), which the guidance channel cannot express today — it prescribes per exact key. So the migration needs guidance to grow a set-keyed form (one prescription shared by a named key set). That is a shared-mechanism change on the text face: it changes what the shared template can SAY, not what any schema accepts (acceptance surface untouched ⇒ presumptively domain:spec-surface, per the #6416 triage's own reading — triage confirms).
Why it is worth doing (the gate blind spot)
alias-integrity.test.ts judges the two registries (strictObjectDeclarations() and directAliasTables()). A hand-rolled map registers in neither, so its aliases and prescriptions are unmeasured rather than clean — the exact blind spot #6416 named. Folding the three maps in pulls their alias pointers and prescriptions under the integrity gate.
#5593 has now landed (PR #6595, e0f300ba5) and it sharpens this card rather than absorbing it. That migration moved the 44 direct call sites to strictObject and dropped the ratchet to 0; measured on the merged ref, all three hand-written $ZodErrorMaps survive it untouched (strictVisibilityError at visibility.ts:113, strictWidgetAnalyticsError in dashboard.zod.ts, strictTenancyError at object.zod.ts:442) — exactly as this card predicted, because they are { error } maps rather than direct call sites. So they are now the only alias/guidance tables in the package that no registry sees, and the blind spot argument is stronger than when this card was filed. Re-price against the landed shape at dispatch: alias-table-registry.ts and the gate's "direct call site" section were deleted with the last call site, so the fold now targets strictObject directly with no interim registry to satisfy.
Inherited guardrails (do not re-derive)
Provenance
Follow-up card for #6416's direction 2, deliberately split out of that card by triage (2026-08-07T17:54:18Z: "Direction 2 is a bigger card, not this one … a shared-mechanism change and deserves its own card rather than arriving as scope creep on the reorder"). Direction 1 (the in-place reorders) landed as PR #6453 (
bf0ae9976).What this card does
Migrate the three hand-written
$ZodErrorMaps to the sharedstrictObject/strictUnknownKeyErrormachinery, deleting the hand-maintained template copies:packages/spec/src/shared/visibility.ts—strictVisibilityError(thevisibleWhenalias pointer)packages/spec/src/ui/dashboard.zod.ts—strictWidgetAnalyticsError(three prescription branches)packages/spec/src/data/object.zod.ts—strictTenancyError(per-key tombstone bullets)The real decision inside it (why it is not mechanical)
dashboard.zod.ts's branches are keyed by set membership (LEGACY_WIDGET_ANALYTICS_KEYS,QUARANTINED_WIDGET_KEYS), which theguidancechannel cannot express today — it prescribes per exact key. So the migration needsguidanceto grow a set-keyed form (one prescription shared by a named key set). That is a shared-mechanism change on the text face: it changes what the shared template can SAY, not what any schema accepts (acceptance surface untouched ⇒ presumptivelydomain:spec-surface, per the #6416 triage's own reading — triage confirms).Why it is worth doing (the gate blind spot)
alias-integrity.test.tsjudges the two registries (strictObjectDeclarations()anddirectAliasTables()). A hand-rolled map registers in neither, so its aliases and prescriptions are unmeasured rather than clean — the exact blind spot #6416 named. Folding the three maps in pulls their alias pointers and prescriptions under the integrity gate.#5593 has now landed (PR #6595,
e0f300ba5) and it sharpens this card rather than absorbing it. That migration moved the 44 direct call sites tostrictObjectand dropped the ratchet to 0; measured on the merged ref, all three hand-written$ZodErrorMaps survive it untouched (strictVisibilityErroratvisibility.ts:113,strictWidgetAnalyticsErrorindashboard.zod.ts,strictTenancyErroratobject.zod.ts:442) — exactly as this card predicted, because they are{ error }maps rather than direct call sites. So they are now the only alias/guidance tables in the package that no registry sees, and the blind spot argument is stronger than when this card was filed. Re-price against the landed shape at dispatch:alias-table-registry.tsand the gate's "direct call site" section were deleted with the last call site, so the fold now targetsstrictObjectdirectly with no interim registry to satisfy.Inherited guardrails (do not re-derive)
view.test.ts/dashboard.test.ts/object.test.tsasserting front-matter → fix channels → explanatory sentence last, plus full-message pins on the no-fix branches. They migrate with the code, never get deleted — they are the ready-made acceptance criteria for this migration.data/object.zod.ts's lazily-built table (objectUnknownKeyErrorImpl ??= …, the TDZ workaround 把 44 个strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593's own card flagged as trap 1) is in this file: check how 把 44 个strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593 resolved it there before assuming this card's fold is mechanical.Provenance
filter别名指向filters—— 一个 ReportSchema 同样拒绝的键(#4001 战役自己的假处方,第 5 例) #5013 / 44 个strictUnknownKeyError直接调用点的别名表在 #5013 闸门覆盖之外(实测干净,但无人看守) #5483 / 把 44 个strictUnknownKeyError直调点批量迁到strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593 (the registries and the migration that ended them).guidance set-keyed strictObject fold, the three file paths,alias-integrity blind spot): no open card.