Skip to content

Commit f7dceed

Browse files
os-zhuangclaude
andauthored
test(spec): alias-integrity fails an alias row a guidanceSet already consumes (#7952)
`strictUnknownKeyError` consults `guidance`, then `guidanceSets`, and only then the `aliases` rename fallback — a guidanceSets match `continue`s past the alias lookup entirely. An alias row whose written key also matches a guidanceSet on the same strict-options table is therefore dead on arrival, and nothing checked that until now. Extends alias-integrity.test.ts with `unreachableAliasRows`, which reads only `options.aliases` / `options.guidanceSets` (never `.shape`) and flags any alias key a guidanceSet already matches. Two tests: the live-table verdict (also pinning that the one existing view-family alias, `disabled -> readonly` on FormFieldSchema, stays reachable outside VISIBILITY_KEY_PATTERN), and a self-test proving the check can go red on a planted synthetic dead row without touching any live schema. Fixes #7889 Claude-Session: https://claude.ai/code/session_01WocN37om5bw81JDoEEMA2e Co-authored-by: Claude <noreply@anthropic.com>
1 parent a44d1b4 commit f7dceed

1 file changed

Lines changed: 133 additions & 0 deletions

File tree

packages/spec/src/shared/alias-integrity.test.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,56 @@ const PROSE_TARGET_SURFACE = /^this `[a-z]+` navigation item$/;
657657
const 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+
660710
describe('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: /vis/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: /vis/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

Comments
 (0)