@ -2,7 +2,7 @@ import { readdirSync, readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path' ;
import { describe , expect , it } from 'vitest' ;
import type { Morfo , Morfo Focus } from './types' ;
import type { Morfo Focus } from './types' ;
/ * *
* The census guard of the focus - first axis ( signed 2026 - 08 - 26 ,
@ -13,13 +13,24 @@ import type { Morfo, MorfoFocus } from './types';
* claimed consumers that did not exist . This census closes the drift in
* BOTH directions :
*
* 1 . a ` trap ` declaration must have ` FocusScope ` wired WITH the policy
* ( and ` trap: true ` species MUST actually use FocusScope ) ;
* 2 . a ` roving ` declaration must consume the shared ` RovingFocusGroup `
* OR carry a signed ` executor.custom ` reason ;
* 3 . the INVERSE ( the author ' s inversion that founded the axis ) : a
* component whose provider uses FocusScope or hand - rolls a roving
* tab stop without declaring ` focus ` fails the census .
* 1 . FORWARD , one rule per kind — a declaration must have the executor the
* kind ' s JSDoc names ( ` MorfoFocus ` in types . ts ) :
* · ` trap ` → ` FocusScope ` wired WITH the policy
* · ` roving ` → ` RovingFocusGroup ` or a signed ` executor.custom `
* · ` walk ` → the directional primitive AND no tab stop managed
* · ` activedescendant ` → ` aria-activedescendant ` declared + ` highlightWalk `
* · ` slider ` → ` valueStep ` , one focusable root , no focus paseo
* · ` segments ` → the segment primitives + a CONSTANT ` tabindex: 0 `
* 2 . the INVERSE ( the author ' s inversion that founded the axis ) : a
* component whose provider shows focus machinery without declaring
* ` focus ` fails the census .
*
* The four kinds beyond ` trap | roving ` arrived 2026 - 08 - 27 ( author - signed
* decision 3 / 3 ) BECAUSE OF THIS FILE : the inverse net parked seven components
* in ` INVERSE_EXCEPTIONS ` with a written reason each , and seven components
* without a name in the contract is an incomplete contract , measured . The
* seven reasons became the four kinds ' specification , so the list below is
* back to the one genuinely COMPOSITIONAL case .
*
* The signed exceptions are listed with their reasons ; growing a list is a
* decision , not a convenience .
@ -28,6 +39,20 @@ import type { Morfo, MorfoFocus } from './types';
const COMPONENTS_DIR = join ( __dirname , '..' , 'soma' , 'components' ) ;
const MORFO_DIR = join ( __dirname , 'components' ) ;
type FocusKind = MorfoFocus [ 'kind' ] ;
/** The closed set the loader parses. Keep in step with `MorfoFocus`. */
const FOCUS_KINDS = [ 'trap' , 'roving' , 'walk' , 'activedescendant' , 'slider' , 'segments' ] as const ;
/ * *
* The tab - stop REGISTRIES : a provider that writes one is managing a single
* stop , whatever it calls itself .
* /
const ROVING_STOP = /RovingFocusGroup|currentTabStop|rovingIndex|rovingTarget/ ;
/** A `tabindex:` object property — the provider WRITING a tab stop. */
const WRITES_TABINDEX = /\btabindex:/ ;
/** kebab -> reason. Declaring morfos whose executor evidence lives elsewhere. */
const EXECUTOR_EXCEPTIONS : Record < string , string > = {
// Composes Dialog: the FocusScope (and its policy wiring) lives in
@ -43,62 +68,63 @@ const EXECUTOR_EXCEPTIONS: Record<string, string> = {
/ * *
* kebab - > reason . Components whose soma sources SHOW focus machinery but
* which legitimately do not declare ` focus ` themselves .
*
* EMPTIED to the compositional case 2026 - 08 - 27 : the seven rows adjudicated
* the day before described REAL focus behaviours the ` trap | roving ` union
* could not name ( a directional walk over native tab stops , the
* aria - activedescendant strategy , the slider model , the segment field ) . They
* are now DECLARED — ` walk ` ( accordion , navigation - menu ) , ` activedescendant `
* ( select , command , combobox ) , ` slider ` ( rating - group ) , ` segments `
* ( date - field , time - field , color - field ) — and a declaring component is
* skipped by the inverse test on its own merit , with no exception row .
* /
const INVERSE_EXCEPTIONS : Record < string , string > = {
// Extends Dialog's provider (see EXECUTOR_EXCEPTIONS) — the declaration
// lives on its own morfo, the machinery on dialog's.
'alert-dialog' : 'machinery belongs to the composed dialog-provider' ,
// ── Adjudicated 2026-08-26 when the hardened net surfaced them: each is
// a REAL focus behaviour that the trap|roving union does not yet name.
// The candidate kinds (`walk`, `activedescendant`, `slider`, `segments`)
// are the axis' logged follow-up — the author's call, never a census
// dodge. Verified against the shipped providers, not assumed:
accordion :
'directional walk over natively tabbable triggers (APG accordion: no roving tabindex — every trigger is a tab stop)' ,
'navigation-menu' :
'disclosure navigation: tabbable triggers walked with arrows, no stop management; focus behaviour owned by the live navigation-menu axis' ,
'rating-group' : 'slider model: one focusable root, arrows move the VALUE, not focus' ,
select :
'aria-activedescendant strategy: virtual highlight (highlightedId), the single DOM stop stays on the anchor' ,
'date-field' :
'segment field: every spinbutton segment is a native tab stop (constant tabindex 0); Left/Right walk focus as a convenience' ,
'time-field' :
'segment field: every spinbutton segment is a native tab stop (constant tabindex 0); Left/Right walk focus as a convenience' ,
'color-field' :
'segment field: every spinbutton segment is a native tab stop (constant tabindex 0); Left/Right walk focus as a convenience'
// lives on its own morfo, the machinery on dialog's. The ONE case that is
// composition rather than an unnamed species.
'alert-dialog' : 'machinery belongs to the composed dialog-provider'
} ;
function loadMorfos ( ) : Array < { kebab : string ; focus : MorfoFocus | undefined } > {
const out : Array < { kebab : string ; focus : MorfoFocus | undefined } > = [ ] ;
interface MorfoRecord {
kebab : string ;
/** The morfo's own source — the activedescendant rule reads it. */
source : string ;
/** `undefined` when the morfo declares no focus policy at all. */
kind : FocusKind | undefined ;
/** The `trap` species default. Meaningless for the other kinds. */
trap : boolean ;
/** Whether the declaration carries `executor: { custom }` (roving only). */
signedExecutor : boolean ;
/** A `focus: {` block the loader could not parse — an instrument failure. */
unparsed : boolean ;
}
function loadMorfos ( ) : MorfoRecord [ ] {
const kindRe = new RegExp (
` \\ n \\ tfocus: \\ { \\ r? \\ n(?: \\ t \\ t//[^ \\ n]* \\ r? \\ n)* \\ t \\ tkind: '( ${ FOCUS_KINDS . join ( '|' ) } )' `
) ;
const out : MorfoRecord [ ] = [ ] ;
for ( const file of readdirSync ( MORFO_DIR ) ) {
if ( ! file . endsWith ( '.ts' ) || file . endsWith ( '.test.ts' ) ) continue ;
const source = readFileSync ( join ( MORFO_DIR , file ) , 'utf-8' ) ;
const kebabMatch = /\n\tkebab: '([a-z0-9-]+)'/ . exec ( source ) ;
if ( ! kebabMatch ) continue ;
const kebab = kebabMatch [ 1 ] ;
const focusMatch = /\n\tfocus: \{/ . test ( source ) ;
if ( ! focusMatch ) {
out . push ( { kebab , focus : undefined } ) ;
const base = { kebab , source , trap : false , signedExecutor : false , unparsed : false } ;
if ( ! /\n\tfocus: \{/ . test ( source ) ) {
out . push ( { . . . base , kind : undefined } ) ;
continue ;
}
const kind = /\n\tfocus: \{\r?\n(?:\t\t\/\/[^\n]*\r?\n)*\t\tkind: '(trap|roving)'/ . exec (
source
) ? . [ 1 ] as MorfoFocus [ 'kind' ] | undefined ;
const trapDefault = /\n\t\ttrap: (true|false)/ . exec ( source ) ? . [ 1 ] ;
const hasCustom = /\n\t\texecutor: \{/ . test ( source ) ;
const kind = kindRe . exec ( source ) ? . [ 1 ] as FocusKind | undefined ;
out . push ( {
kebab ,
focus :
kind === 'trap'
? ( { kind : 'trap' , trap : trapDefault === 'true' } as MorfoFocus )
: kind === 'roving'
? ( {
kind : 'roving' ,
parts : [ ] ,
orientation : 'horizontal' ,
. . . ( hasCustom ? { executor : { custom : 'signed' } } : { } )
} as MorfoFocus )
: undefined
. . . base ,
kind ,
// A declared block whose kind the loader cannot read is NOT "no
// declaration": it would silently move the component into the inverse
// net. The instrument lies before the code does, so it is reported.
unparsed : kind === undefined ,
trap : /\n\t\ttrap: (true|false)/ . exec ( source ) ? . [ 1 ] === 'true' ,
signedExecutor : /\n\t\texecutor: \{/ . test ( source )
} ) ;
}
return out ;
@ -122,24 +148,31 @@ function somaSources(kebab: string): string {
describe ( 'focus census — declaration ↔ executor, both directions' , ( ) = > {
const morfos = loadMorfos ( ) ;
const declaring = morfos . filter ( ( m ) = > m . focus !== undefined ) ;
const declaring = morfos . filter ( ( m ) = > m . kind !== undefined ) ;
it ( 'the census inspects a real corpus (guard against the empty sweep)' , ( ) = > {
expect ( morfos . length ) . toBeGreaterThan ( 150 ) ;
expect ( declaring . length ) . toBeGreaterThanOrEqual ( 28 ) ;
expect ( declaring . length ) . toBeGreaterThanOrEqual ( 37 ) ;
// Every kind of the union has at least one declared member: a kind with
// zero population is a contract nobody measured.
expect ( [ . . . new Set ( declaring . map ( ( m ) = > m . kind ) ) ] . sort ( ) ) . toEqual ( [ . . . FOCUS_KINDS ] . sort ( ) ) ;
} ) ;
it ( 'the loader reads every focus block it finds (the instrument lies first)' , ( ) = > {
expect ( morfos . filter ( ( m ) = > m . unparsed ) . map ( ( m ) = > m . kebab ) ) . toEqual ( [ ] ) ;
} ) ;
it ( 'every trap declaration has FocusScope wired with the declared policy' , ( ) = > {
const offenders : string [ ] = [ ] ;
for ( const m of declaring ) {
if ( m . focus ? . kind !== 'trap' ) continue ;
if ( m . kind !== 'trap' ) continue ;
if ( EXECUTOR_EXCEPTIONS [ m . kebab ] ) continue ;
const src = somaSources ( m . kebab ) ;
const usesScope = src . includes ( 'FocusScope.use' ) ;
const wiresPolicy = /policy:.*runtime\.focus/ . test ( src ) ;
// trap:false species may legitimately skip FocusScope entirely
// (nothing to trap); but if the scope IS used, the policy must ride.
if ( m . focus. trap && ! usesScope ) offenders . push ( ` ${ m . kebab } : trap:true without FocusScope ` ) ;
if ( m . trap && ! usesScope ) offenders . push ( ` ${ m . kebab } : trap:true without FocusScope ` ) ;
if ( usesScope && ! wiresPolicy ) offenders . push ( ` ${ m . kebab } : FocusScope without policy ` ) ;
}
expect ( offenders ) . toEqual ( [ ] ) ;
@ -148,8 +181,8 @@ describe('focus census — declaration ↔ executor, both directions', () => {
it ( 'every roving declaration consumes the shared primitive or signs its executor' , ( ) = > {
const offenders : string [ ] = [ ] ;
for ( const m of declaring ) {
if ( m . focus? . kind !== 'roving' ) continue ;
if ( m . focus. e xecutor) continue ; // signed custom — visible in the morfo
if ( m . kind !== 'roving' ) continue ;
if ( m . signedE xecutor) continue ; // signed custom — visible in the morfo
if ( EXECUTOR_EXCEPTIONS [ m . kebab ] ) continue ;
const src = somaSources ( m . kebab ) ;
if ( ! src . includes ( 'RovingFocusGroup' ) ) {
@ -159,6 +192,93 @@ describe('focus census — declaration ↔ executor, both directions', () => {
expect ( offenders ) . toEqual ( [ ] ) ;
} ) ;
it ( 'every walk declaration walks NATIVE tab stops — arrows as convenience, no stop managed' , ( ) = > {
const offenders : string [ ] = [ ] ;
for ( const m of declaring ) {
if ( m . kind !== 'walk' ) continue ;
const src = somaSources ( m . kebab ) ;
if ( ! /getDirectionalKeys/ . test ( src ) ) {
offenders . push ( ` ${ m . kebab } : walk without the directional primitive ` ) ;
}
// The species IS the absence of stop management: the parts are
// tabbable on their own. A provider writing `tabindex` is declaring
// the wrong kind — that is `roving`.
if ( WRITES_TABINDEX . test ( src ) ) {
offenders . push ( ` ${ m . kebab } : walk writes a tabindex — that is a roving stop, not a walk ` ) ;
}
if ( ROVING_STOP . test ( src ) ) {
offenders . push ( ` ${ m . kebab } : walk manages a roving tab stop ` ) ;
}
}
expect ( offenders ) . toEqual ( [ ] ) ;
} ) ;
it ( 'every activedescendant declaration ships the virtual highlight it claims' , ( ) = > {
const offenders : string [ ] = [ ] ;
for ( const m of declaring ) {
if ( m . kind !== 'activedescendant' ) continue ;
// The attr IS the species: without it the "virtual focus" reaches no
// assistive technology at all.
if ( ! m . source . includes ( "attr: 'aria-activedescendant'" ) ) {
offenders . push ( ` ${ m . kebab } : activedescendant without an aria-activedescendant declaration ` ) ;
}
const src = somaSources ( m . kebab ) ;
if ( ! src . includes ( 'highlightWalk' ) ) {
offenders . push ( ` ${ m . kebab } : activedescendant without highlight machinery ` ) ;
}
// The single DOM stop stays on the anchor: a roving registry here
// would mean the highlight and a real tab stop both move.
if ( ROVING_STOP . test ( src ) ) {
offenders . push ( ` ${ m . kebab } : activedescendant that also manages a roving tab stop ` ) ;
}
}
expect ( offenders ) . toEqual ( [ ] ) ;
} ) ;
it ( 'every slider declaration has ONE focusable root and moves the value, not the focus' , ( ) = > {
const offenders : string [ ] = [ ] ;
for ( const m of declaring ) {
if ( m . kind !== 'slider' ) continue ;
const src = somaSources ( m . kebab ) ;
if ( ! src . includes ( 'valueStep' ) ) {
offenders . push ( ` ${ m . kebab } : slider without the value-step primitive ` ) ;
}
// One focusable root: something in the component takes tabindex 0.
if ( ! /\btabindex:[^\n]*\b0\b/ . test ( src ) ) {
offenders . push ( ` ${ m . kebab } : slider without a focusable root ` ) ;
}
if ( ROVING_STOP . test ( src ) || /segment(Walk|Advance|Retreat)|gridWalk/ . test ( src ) ) {
offenders . push ( ` ${ m . kebab } : slider that manages tab stops ` ) ;
}
// The pair-signal: directional keys COMBINED with a focus move is a
// focus paseo, and this species does not have one.
if ( /getDirectionalKeys/ . test ( src ) && /(dom\.focus\(|\.focus\(\))/ . test ( src ) ) {
offenders . push ( ` ${ m . kebab } : slider walks focus with directional keys ` ) ;
}
}
expect ( offenders ) . toEqual ( [ ] ) ;
} ) ;
it ( 'every segments declaration keeps a CONSTANT tabindex 0 on its segments' , ( ) = > {
const offenders : string [ ] = [ ] ;
for ( const m of declaring ) {
if ( m . kind !== 'segments' ) continue ;
const src = somaSources ( m . kebab ) ;
if ( ! /segment(Walk|Advance|Retreat)/ . test ( src ) ) {
offenders . push ( ` ${ m . kebab } : segments without the segment-walk primitives ` ) ;
}
// The invariant that separates this species from `roving`: the stop is
// never taken away, so the shared segment attrs carry a literal 0.
if ( ! /\btabindex: 0\b/ . test ( src ) ) {
offenders . push ( ` ${ m . kebab } : segments without the constant tabindex 0 ` ) ;
}
if ( ROVING_STOP . test ( src ) ) {
offenders . push ( ` ${ m . kebab } : segments that manages a roving tab stop ` ) ;
}
}
expect ( offenders ) . toEqual ( [ ] ) ;
} ) ;
it ( 'the INVERSE: focus machinery without a declaration fails (the axis’ founding rule)' , ( ) = > {
const declared = new Set ( declaring . map ( ( m ) = > m . kebab ) ) ;
const offenders : string [ ] = [ ] ;
@ -167,15 +287,12 @@ describe('focus census — declaration ↔ executor, both directions', () => {
if ( INVERSE_EXCEPTIONS [ m . kebab ] ) continue ;
const src = somaSources ( m . kebab ) ;
if ( ! src ) continue ;
// The primitive/scope imports catch executor users; the pattern
// catches hand-rolled FOCUS-STOP machinery. Deliberately NOT
// `getDirectionalKeys` alone — arrows that move a VALUE (rating,
// media seek) or an aria-activedescendant HIGHLIGHT (select,
// command) are not tab-stop machinery; the activedescendant
// strategy is the axis' logged follow-up (a third `kind`, the
// author's call), not this census' net. Hand-rolled focus movement
// shows as directional keys COMBINED with a stop/`.focus()` walk —
// the tab-stop registry names are the reliable signal.
// The net catches two populations: the EXECUTORS of a named species
// (each kind of `MorfoFocus` names one) and hand-rolled tab-stop
// machinery. Deliberately NOT `getDirectionalKeys` alone — arrows that
// move a VALUE (rating, media seek) are not tab-stop machinery; a
// hand-rolled focus walk shows as directional keys COMBINED with a
// `.focus()`, which is the pair-signal at the bottom.
const machinery =
src . includes ( 'FocusScope.use' ) ||
src . includes ( 'RovingFocusGroup' ) ||
@ -189,31 +306,25 @@ describe('focus census — declaration ↔ executor, both directions', () => {
// it absorbed that pair out of date-field / time-field / color-field.
// All three names, not just the walk — a field that only auto-advances
// (`segmentAdvance`) or only retreats on delete (`segmentRetreat`) is
// moving focus just the same.
// moving focus just the same. Those three now DECLARE `kind:
// 'segments'`, so this line guards the next one instead of them.
/segment(Walk|Advance|Retreat)/ . test ( src ) ||
// The THIRD wave-C+ primitive, `$soma/keyboard/highlight-walk`, is
// deliberately ABSENT from this list. It absorbed the key tables of
// select / command / combobox, but what it moves is the virtual
// HIGHLIGHT the paragraph above excludes by name — it has no focus
// port at all, and the anchor keeps the single DOM stop. Adding
// `highlightWalk` here would make the net catch the very species this
// census decided is not tab-stop machinery. Note that absorbing those
// tables also takes `getDirectionalKeys` out of select-provider and
// command-provider, so neither trips the pair-signal below any more;
// select's INVERSE_EXCEPTIONS row stays because it states the SPECIES,
// not a symptom (and its reason is still literally true).
//
// The FOURTH, `$soma/keyboard/value-step`, is absent for the same
// reason and by the same sentence: what it moves is the VALUE the
// paragraph above excludes by name, on a control that has ONE focusable
// stop and never moves it. Adding `valueStep` here would catch the very
// species this census decided is not tab-stop machinery. It absorbed
// `getDirectionalKeys` out of rating-group's and slider's providers, so
// rating-group no longer trips the pair-signal below (it did until now,
// through the `dom.focus` that returns focus to the root after a click);
// its INVERSE_EXCEPTIONS row stays because, like select's, it states the
// SPECIES — «slider model: one focusable root, arrows move the VALUE,
// not focus» — and that is still literally true.
// `$soma/keyboard/highlight-walk` JOINED the net 2026-08-27. It used
// to be excluded by name — what it moves is a virtual highlight, not
// a tab stop — and the exclusion was correct while the contract had
// no word for that species. `kind: 'activedescendant'` is that word,
// so the executor becomes evidence like any other: select, command
// and combobox all declare, and the line costs nothing today while
// making the fourth member impossible to add in silence.
src . includes ( 'highlightWalk' ) ||
// `$soma/keyboard/value-step` stays OUT, and this is the one place
// the decision is written down rather than assumed: the species has
// a name now (`kind: 'slider'`, rating-group declares it), but the
// primitive's population is SIX (rating-group, slider, number-field,
// knob, css-field, gradient-builder) and decision 3/3 measured only
// rating-group. Adding `valueStep` here would fail five components
// whose species nobody has measured yet — a scope cut announced, not
// taken in silence. Measure them, declare them, then add the line.
/currentTabStop|rovingIndex|rovingTarget/ . test ( src ) ||
( /getDirectionalKeys/ . test ( src ) && /(dom\.focus\(|\.focus\(\))/ . test ( src ) ) ;
if ( machinery ) {