@@ -657,6 +657,56 @@ const PROSE_TARGET_SURFACE = /^this `[a-z]+` navigation item$/;
657657const isProseTarget = ( surface : string , target : string ) : boolean =>
658658 PROSE_TARGET_SURFACE . test ( surface ) && PROSE_ALIAS_TARGETS . has ( target ) ;
659659
660+ /**
661+ * Alias rows made UNREACHABLE by a `guidanceSet` in the same strict options
662+ * table (#7889).
663+ *
664+ * `strictUnknownKeyError` (`suggestions.zod.ts`) consults three channels per
665+ * unrecognized key, in order: exact `guidance`, then `guidanceSets`, and only
666+ * THEN the `aliases` rename fallback. A `guidanceSets` match `continue`s past
667+ * the alias lookup entirely — it never runs for that key. So an alias row
668+ * whose WRITTEN key also matches a guidanceSet declared on the same table is
669+ * dead on arrival: the set answers first, every time, and the alias entry
670+ * reads as coverage for a spelling nobody is actually helped with.
671+ *
672+ * Not hypothetical: PR #7884 had to place its one view-family alias
673+ * (`disabled → readonly`, `ui/view.zod.ts`) OUTSIDE `VISIBILITY_KEY_PATTERN`'s
674+ * reach by hand, because nothing checked it — a `visible: 'visibleWhen'` or
675+ * `showWhen: 'visibleWhen'` row on the same table would have shipped exactly
676+ * as dead, with every existing gate green (#7889).
677+ *
678+ * The pattern-vs-list asymmetry the checks above already draw — an enumerated
679+ * `keys: string[]` can be judged against the DECLARED shape, a `keys: RegExp`
680+ * cannot (`VISIBILITY_KEY_PATTERN` deliberately also matches the canonical
681+ * `visibleWhen`, which the shape declares and which therefore never reaches
682+ * either channel) — does not apply here. An alias's written key is a concrete
683+ * authored string, not a member of the shape, so it can be tested against a
684+ * pattern set exactly as a real rejected key would be: `keySetMatches` is the
685+ * same function `strictUnknownKeyError` itself calls.
686+ *
687+ * Reads only `options.aliases` and `options.guidanceSets` — never `.shape` —
688+ * so it composes with (and does not duplicate) the declared-key checks above.
689+ */
690+ function unreachableAliasRows ( surfaces : readonly StrictObjectDeclaration [ ] ) : string [ ] {
691+ const dead : string [ ] = [ ] ;
692+ for ( const s of surfaces ) {
693+ const sets = s . options . guidanceSets ?? [ ] ;
694+ if ( sets . length === 0 ) continue ;
695+ for ( const [ written , target ] of Object . entries ( s . options . aliases ?? { } ) ) {
696+ const set = sets . find ( ( candidate ) => keySetMatches ( candidate , written ) ) ;
697+ if ( set ) {
698+ dead . push (
699+ `"${ s . options . surface } ": alias \`${ written } \` -> \`${ target } \` is unreachable — `
700+ + `guidanceSet \`${ set . name } \` already matches \`${ written } \` and consumes it before `
701+ + 'the alias table is ever consulted (drop the alias row, or fold '
702+ + `\`${ written } \` into \`${ set . name } \`'s prescription instead)` ,
703+ ) ;
704+ }
705+ }
706+ }
707+ return dead ;
708+ }
709+
660710describe ( 'alias integrity — every table is a true claim about its schema' , ( ) => {
661711 it ( 'no alias key is itself a declared key (a dead entry that can never fire)' , ( ) => {
662712 // An alias is consulted only from the `unrecognized_keys` path. A key the
@@ -827,6 +877,89 @@ describe('alias integrity — every table is a true claim about its schema', ()
827877 expect ( broken . sort ( ) ) . toEqual ( [ ] ) ;
828878 } ) ;
829879
880+ it ( 'no alias row is dead on arrival because a guidanceSet in the same table already consumes it (#7889)' , ( ) => {
881+ // The live-table verdict. If this ever turns red on a real schema, the fix
882+ // is at the authoring site (drop the row, or fold the key into the set's
883+ // prescription) — never here, and never a change to the predicate that
884+ // makes the row stop being found.
885+ expect ( unreachableAliasRows ( SURFACES ) . sort ( ) ) . toEqual ( [ ] ) ;
886+
887+ // The acceptance shape's second half: the one existing view-family alias
888+ // row (`disabled -> readonly` on `FormFieldSchema`, `ui/view.zod.ts:1775`)
889+ // is confirmed to exist and to sit on `VISIBILITY_STRICT_OPTIONS`'s
890+ // `VISIBILITY_KEY_PATTERN` table — so the assertion above is judging the
891+ // real, live, non-trivial case (a table that DOES carry a pattern-matching
892+ // guidanceSet), not an empty one that would pass vacuously.
893+ const visibilityTablesWithThisAlias = SURFACES . filter (
894+ ( s ) =>
895+ s . options . aliases ?. disabled === 'readonly'
896+ && ( s . options . guidanceSets ?? [ ] ) . some ( ( set ) => set . name === 'VISIBILITY_KEY_PATTERN' ) ,
897+ ) ;
898+ expect (
899+ visibilityTablesWithThisAlias . length ,
900+ 'the FormFieldSchema `disabled -> readonly` row should exist and carry VISIBILITY_KEY_PATTERN' ,
901+ ) . toBeGreaterThan ( 0 ) ;
902+ } ) ;
903+
904+ it ( 'the guidanceSet-reachability check can actually go red — a planted dead row, no live schema touched (#7889)' , ( ) => {
905+ // Self-test, per the triage ruling: prove the gate can fail before trusting
906+ // that it passing on the live table means anything. Entirely synthetic —
907+ // `unreachableAliasRows` only reads `options.aliases` / `options.guidanceSets`,
908+ // so `shape` is never inspected and can be an empty stand-in.
909+ const emptyShape = { } as StrictObjectDeclaration [ 'shape' ] ;
910+
911+ const planted : StrictObjectDeclaration [ ] = [ {
912+ options : {
913+ surface : 'synthetic reachability probe' ,
914+ history : 'n/a — planted for #7889 self-test' ,
915+ aliases : { visibleIf : 'visibleWhen' } ,
916+ guidanceSets : [ {
917+ name : 'SYNTHETIC_VIS_PATTERN' ,
918+ keys : / v i s / i,
919+ examples : [ 'visibleIf' ] ,
920+ prescription : 'n/a' ,
921+ } ] ,
922+ } ,
923+ shape : emptyShape ,
924+ } ] ;
925+ const dead = unreachableAliasRows ( planted ) ;
926+ expect ( dead ) . toHaveLength ( 1 ) ;
927+ expect ( dead [ 0 ] ) . toContain ( 'visibleIf' ) ;
928+ expect ( dead [ 0 ] ) . toContain ( 'SYNTHETIC_VIS_PATTERN' ) ;
929+
930+ // Control: the identical alias row with NO guidanceSet on the table is
931+ // reachable — proves the planted row above went red because of the
932+ // pattern match, not because of anything else about the shape.
933+ const reachable : StrictObjectDeclaration [ ] = [ {
934+ options : {
935+ surface : 'synthetic reachability probe (no set)' ,
936+ history : 'n/a — planted for #7889 self-test' ,
937+ aliases : { visibleIf : 'visibleWhen' } ,
938+ } ,
939+ shape : emptyShape ,
940+ } ] ;
941+ expect ( unreachableAliasRows ( reachable ) ) . toEqual ( [ ] ) ;
942+
943+ // Second control: a guidanceSet present but NOT matching the alias key —
944+ // proves the row is judged by an actual pattern match, not merely by the
945+ // presence of a guidanceSets array on the table.
946+ const nonMatching : StrictObjectDeclaration [ ] = [ {
947+ options : {
948+ surface : 'synthetic reachability probe (non-matching set)' ,
949+ history : 'n/a — planted for #7889 self-test' ,
950+ aliases : { disabled : 'readonly' } ,
951+ guidanceSets : [ {
952+ name : 'SYNTHETIC_VIS_PATTERN' ,
953+ keys : / v i s / i,
954+ examples : [ 'visibleIf' ] ,
955+ prescription : 'n/a' ,
956+ } ] ,
957+ } ,
958+ shape : emptyShape ,
959+ } ] ;
960+ expect ( unreachableAliasRows ( nonMatching ) ) . toEqual ( [ ] ) ;
961+ } ) ;
962+
830963 it ( 'the three #6416 hand-written maps are FOLDED and judged here — the blind spot stays closed (#6619)' , ( ) => {
831964 // The reason #6619 existed: `strictVisibilityError`,
832965 // `strictWidgetAnalyticsError` and `strictTenancyError` were hand-rolled
0 commit comments