morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
/ * *
* Morfo DOM - validation check .
*
* For each morfo at ` src/uix/morfo/components/*.ts ` :
* 1 . Navigate to the matching routed demo at ` /uix/components/{kebab} ` .
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
* 2 . For every ` public ` part declared in the morfo , query the DOM for
* ` [data-{component}-{part}] ` ( or ` [data-{component}] ` for root ) .
* 3 . For each matching element , verify :
* - Every data - attr the morfo declares with ` severity: 'required' `
* is present .
* - Every data - attr with ` values: [...] ` has a value in that set .
* - No ` data-{component}-* ` attr is present that isn ' t declared in
* the morfo ( strict mode — excludes ` data-_* ` privates ) .
*
* Runs after ` npm run smoke ` passes . Requires ` npm run dev ` running .
* Morfos without a current routed demo are reported as SKIP .
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
*
* Exit codes :
* 0 — all routed morfos validate against their rendered DOM .
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
* 1 — at least one morfo - vs - DOM discrepancy .
* 2 — no dev server / couldn ' t load morfos / no routed morfos .
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
* /
import { chromium , type Page } from 'playwright' ;
import { EIDOS_ONLY_ATTRS } from './eidos-only-attrs' ;
import { existsSync , readdirSync } from 'node:fs' ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
import { fileURLToPath , pathToFileURL } from 'node:url' ;
import { dirname , join } from 'node:path' ;
import type { Morfo , MorfoPart , MorfoData } from '../src/uix/morfo/types' ;
import { validateMorfo } from '../src/uix/morfo/schema' ;
const __dirname = dirname ( fileURLToPath ( import . meta . url ) ) ;
const MORFOS_DIR = join ( __dirname , '..' , 'src' , 'uix' , 'morfo' , 'components' ) ;
const ROUTES_DIR = join ( __dirname , '..' , 'web' , 'routes' ) ;
const DEFAULT_ROUTE_PREFIX = '/uix/components' ;
const routePrefix = normaliseRoutePrefix ( process . env . MORFO_ROUTE_PREFIX ? ? DEFAULT_ROUTE_PREFIX ) ;
function normaliseRoutePrefix ( value : string ) : string {
const trimmed = value . trim ( ) . replace ( /\/+$/ , '' ) ;
if ( ! trimmed || trimmed === '/' ) return '' ;
return trimmed . startsWith ( '/' ) ? trimmed : ` / ${ trimmed } ` ;
}
function routeToPageFile ( route : string ) : string {
const segments = route
. split ( '/' )
. filter ( Boolean )
. filter ( ( segment ) = > ! ( segment . startsWith ( '(' ) && segment . endsWith ( ')' ) ) ) ;
return join ( ROUTES_DIR , . . . segments , '+page.svelte' ) ;
}
function routeForMorfo ( morfo : Morfo ) : string | null {
const route = ` ${ routePrefix } / ${ morfo . kebab } ` . replace ( /\/+/g , '/' ) ;
return existsSync ( routeToPageFile ( route ) ) ? route : null ;
}
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
async function probePort ( start : number , end : number ) : Promise < string | null > {
for ( let port = start ; port <= end ; port ++ ) {
try {
const res = await fetch ( ` http://localhost: ${ port } / ` , {
signal : AbortSignal.timeout ( 500 )
} ) ;
if ( res . ok || res . status === 404 || res . status === 500 ) {
return ` http://localhost: ${ port } ` ;
}
} catch {
// try next
}
}
return null ;
}
async function loadMorfos ( ) : Promise < Morfo [ ] > {
const files = readdirSync ( MORFOS_DIR ) . filter ( ( f ) = > f . endsWith ( '.ts' ) && ! f . endsWith ( '.test.ts' ) ) ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const out : Morfo [ ] = [ ] ;
for ( const f of files ) {
// Use file:// URL on Windows — absolute paths starting with "g:" are
// rejected by Node's ESM loader.
const url = pathToFileURL ( join ( MORFOS_DIR , f ) ) . href ;
const mod = ( await import ( url ) ) as Record < string , unknown > ;
for ( const v of Object . values ( mod ) ) {
if ( typeof v === 'object' && v !== null && 'kebab' in v && 'parts' in v ) {
out . push ( validateMorfo ( v ) ) ;
}
}
}
return out ;
}
type Issue = {
kind : 'missing' | 'bad-value' | 'undeclared' ;
message : string ;
/ * *
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
* The house ' s instrument - rollout pattern ( P0 lesson : measure , adjudicate ,
* THEN gate ) : a new pass sets this flag so its findings print and tally
* WITHOUT failing the run , until its first harvest is adjudicated — then
* the flag is deleted from that pass and it gates . The ARIA pass was born
* advisory 2026 - 08 - 26 ( 258 findings ) , its crop was adjudicated through the
* unified dossier ( fila cero : the composition edge ) , and it FLIPPED to
* gating 2026 - 08 - 27 at zero . Nothing sets this today ; the plumbing stays
* for the next pass ' s rollout .
* /
advisory? : boolean ;
} ;
/ * *
* ARIA attrs the CONSUMER legally owns ( the A - 85 naming class : consumer - first
* by vocabulary , no framework default ) — their presence on a part without a
* morfo declaration is not drift .
* /
const CONSUMER_NAMING_ARIA = new Set ( [ 'aria-label' , 'aria-labelledby' , 'aria-describedby' ] ) ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
/ * *
* SIGNED exceptions of the ARIA pass — ` "kebab.part" -> attr -> reason ` .
* Growing this map is a decision with an acta , never a convenience ( the
* focus / keyboard census discipline ) . Each row cites its cure ' s owner .
* /
const ARIA_PASS_EXCEPTIONS : Record < string , Record < string , string > > = {
// The DEMO wires a combobox-shaped harness onto the input
// (+page.svelte:312-315) — consumer ARIA on a framework element, which
// this instrument cannot tell from component ARIA without an edge. Cure
// owned by the demo (dossier L5-side, sources lane).
'search-field.input' : {
'aria-controls' : 'demo-written combobox harness — cure is the demo’ s' ,
'aria-expanded' : 'demo-written combobox harness — cure is the demo’ s' ,
'aria-autocomplete' : 'demo-written combobox harness — cure is the demo’ s'
} ,
// The eidos shell hand-writes the status div instead of composing
// Palabras.Status, DELIBERATELY: composing would make the whole bar a
// live region announcing on every keystroke, against palabras’ own
// WCAG 4.1.3 doctrine. Shared-branch territory (palabras axis) — the
// declaration is true of the soma part; the shell’ s hand div is the
// open row (dossier, causa 6).
'palabras.status' : {
'aria-live' : 'eidos shell hand-writes the div (4.1.3 doctrine) — palabras-axis row'
}
} ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
/** Flatten morfo parts into a flat array for validation lookup. */
function flatParts ( parts : readonly MorfoPart [ ] ) : MorfoPart [ ] {
const out : MorfoPart [ ] = [ ] ;
for ( const p of parts ) {
out . push ( p ) ;
if ( p . parts && p . parts . length > 0 ) out . push ( . . . flatParts ( p . parts ) ) ;
}
return out ;
}
function dataAttrFor ( kebab : string , part : MorfoPart ) : string {
return part . kebab === 'provider' ? ` data- ${ kebab } ` : ` data- ${ kebab } - ${ part . kebab } ` ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
/ * *
* THE EDGE ( fila cero , author - signed 2026 - 08 - 27 ) : when a host part SHARES its
* DOM element with a composed component ( ` chat-message.reaction-add ` IS the
* ` Popover.Trigger ` ) , the composed component ' s ARIA belongs to ITS morfo , not
* the host ' s . The identity already travels through the single attr pipeline :
* every composed part stamps its namespaced identifying attr on the same
* element — so the edge is resolved from CO - LOCATED part - identifying attrs
* ( this is the data pass 's "Allow other components' part attrs " clause ,
* generalized to the ARIA those parts declare ) . No new contract surface .
*
* Declared limits ( named , not covered ) :
* - Consumer - written ARIA on a framework element ( search - field ' s demo ) stays
* flagged — the cure is the demo ' s , the instrument cannot tell consumers
* from components without an edge .
* - A part that never renders in any routed demo ( RateButton class ) is
* invisible to ANY render - based resolution — this instrument measures what
* mounts .
* - Two co - located contracts declaring the SAME attr with different values is
* a future check ( named in the dossier ) , not this clause .
* /
type EdgeIndex = Map < string , { kebab : string ; part : MorfoPart } > ;
function buildEdgeIndex ( all : Morfo [ ] ) : EdgeIndex {
const index : EdgeIndex = new Map ( ) ;
for ( const m of all ) {
for ( const p of flatParts ( m . parts ) . filter ( ( p ) = > p . kind === 'public' ) ) {
const attr = dataAttrFor ( m . kebab , p ) ;
// `data-a-b` can name both a.b and a-b.provider. Zero collisions in
// today's 919-attr corpus (measured 2026-08-27); the day one is
// born, the edge would resolve the WRONG contract in silence —
// throw instead and force the adjudication.
const prior = index . get ( attr ) ;
if ( prior ) {
throw new Error (
` edge-index collision: " ${ attr } " names both ${ prior . kebab } . ${ prior . part . kebab } and ${ m . kebab } . ${ p . kebab } `
) ;
}
index . set ( attr , { kebab : m.kebab , part : p } ) ;
}
}
return index ;
}
feat(morfo)!: decisión 7 — `value` es OPCIONAL en aria[]/attrs[]: declarar la EXISTENCIA sin poseer el valor
Firmada por el autor (bolsa de 12, 2026-08-27). La asimetría con data[] —que
siempre admitió la entrada sin fuente— hacía INEXPRESABLE la cura del lote L5
(un attr cuya aritmética el morfo no puede computar: depth+1, colIndex+1);
un constructor lo midió con tsc y paró. Ahora el contrato puede decir «este
attr existe, el provider posee su valor», que es la forma que data[] practica.
- types.ts: `value?`, con la doctrina en el JSDoc.
- compile.ts: guard `if (!entry.value) continue` en ambos cubos, réplica
LITERAL del de data[] (:452): sin plan y sin deps, pero la entrada consta.
- schema.ts: `value` opcional + invariante nuevo — `ariaBoolean` sin value
LANZA (pedir stringificación booleana de un valor inexistente es
declaración incoherente).
- resolver.ts: consecuencia OBLIGADA que el lote destapó — `resolvePartAttrs`
escribía `undefined` en la bolsa para una entrada sin fuente, y `dom.apply`
lee undefined como BORRA: habría pisado el valor del provider (la ley de
wave por la otra puerta). Ahora salta, simétrico a data.
- morfo-check: el pase ARIA ya distinguía «declarado sin fuente» de «no
declarado» por construcción (declaredAria mapea por attr); queda la ley
escrita y la mitad «required sin condición» SIGUE exigiéndose: que el
provider posea el valor es la promesa de que emite uno.
PROVENIENCIA CORREGIDA en los 3 sitios (JSDoc de schema, JSDoc del flag y el
MENSAJE QUE SE LANZA): citaban la regresión f48acab37 como la clase que el
invariante cierra y NO la cierra — aquella entrada TENÍA value (stateRef, sin
flag), así que retorna antes; la mentira entró por la REGISTRACIÓN, que un
esquema no puede ver porque no ve al provider. Esa clase la cierran la
doctrina de composición y el censo de fuentes. Medido: las 114 apariciones
del flag llevan todas value ⇒ hoy el invariante no muerde ninguna. Higiene
contra una forma, con su proveniencia honesta. (Hallazgo del peer vicen-e1,
verificado por mí con git show.)
Corpus medido: 168 morfos / 994 entradas / 0 sin value hoy — la puerta se
abre vacía a propósito. morfo 270/270 · server 1850 · client soma 643 ·
check 71 (techo).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
// Tally of the parts a run could not look at (declared here because the
// validator increments them; printed in the summary so the headline can never
// speak for parts that never mounted).
let unmountedParts = 0 ;
const unmountedMorfos = new Set < string > ( ) ;
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
async function validateMorfoAgainstDom (
page : Page ,
morfo : Morfo ,
edgeIndex : EdgeIndex
) : Promise < Issue [ ] > {
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const issues : Issue [ ] = [ ] ;
const parts = flatParts ( morfo . parts ) . filter ( ( p ) = > p . kind === 'public' ) ;
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
// Collect every part-identifying attr across the morfo so a part can
// legitimately carry a sibling's identifying attr (e.g. tag-group Link
// is a variant of Item, the <a> element carries both data-tag-group-link
// and data-tag-group-item).
const allPartAttrs = new Set ( parts . map ( ( p ) = > dataAttrFor ( morfo . kebab , p ) ) ) ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
for ( const part of parts ) {
const partAttr = dataAttrFor ( morfo . kebab , part ) ;
// All elements on the page that carry this part's identifying attr.
// data-* feeds the gating pass; role/aria-* feeds the advisory aria pass.
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const elementsAttrs = await page . $ $eval ( ` [ ${ partAttr } ] ` , ( nodes ) = >
nodes . map ( ( el ) = >
Array . from ( el . attributes )
. filter (
( a ) = > a . name . startsWith ( 'data-' ) || a . name . startsWith ( 'aria-' ) || a . name === 'role'
)
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
. map ( ( a ) = > ( { name : a.name , value : a.value } ) )
)
) ;
if ( elementsAttrs . length === 0 ) {
// Not on the demo page — skip validation entirely. Morfo "optional"
// vs "required" is about composition validity (whether a consumer
// MAY omit the part), not about demo-page presence. Dialog.Content
// is required in a valid composition but only mounts when
// `open=true`, which the static demo page may not exercise.
//
// A dedicated permutation-matrix script (future) will exercise
// each component through its state space and validate part
// presence per permutation. For now, demo presence is advisory.
feat(morfo)!: decisión 7 — `value` es OPCIONAL en aria[]/attrs[]: declarar la EXISTENCIA sin poseer el valor
Firmada por el autor (bolsa de 12, 2026-08-27). La asimetría con data[] —que
siempre admitió la entrada sin fuente— hacía INEXPRESABLE la cura del lote L5
(un attr cuya aritmética el morfo no puede computar: depth+1, colIndex+1);
un constructor lo midió con tsc y paró. Ahora el contrato puede decir «este
attr existe, el provider posee su valor», que es la forma que data[] practica.
- types.ts: `value?`, con la doctrina en el JSDoc.
- compile.ts: guard `if (!entry.value) continue` en ambos cubos, réplica
LITERAL del de data[] (:452): sin plan y sin deps, pero la entrada consta.
- schema.ts: `value` opcional + invariante nuevo — `ariaBoolean` sin value
LANZA (pedir stringificación booleana de un valor inexistente es
declaración incoherente).
- resolver.ts: consecuencia OBLIGADA que el lote destapó — `resolvePartAttrs`
escribía `undefined` en la bolsa para una entrada sin fuente, y `dom.apply`
lee undefined como BORRA: habría pisado el valor del provider (la ley de
wave por la otra puerta). Ahora salta, simétrico a data.
- morfo-check: el pase ARIA ya distinguía «declarado sin fuente» de «no
declarado» por construcción (declaredAria mapea por attr); queda la ley
escrita y la mitad «required sin condición» SIGUE exigiéndose: que el
provider posea el valor es la promesa de que emite uno.
PROVENIENCIA CORREGIDA en los 3 sitios (JSDoc de schema, JSDoc del flag y el
MENSAJE QUE SE LANZA): citaban la regresión f48acab37 como la clase que el
invariante cierra y NO la cierra — aquella entrada TENÍA value (stateRef, sin
flag), así que retorna antes; la mentira entró por la REGISTRACIÓN, que un
esquema no puede ver porque no ve al provider. Esa clase la cierran la
doctrina de composición y el censo de fuentes. Medido: las 114 apariciones
del flag llevan todas value ⇒ hoy el invariante no muerde ninguna. Higiene
contra una forma, con su proveniencia honesta. (Hallazgo del peer vicen-e1,
verificado por mí con git show.)
Corpus medido: 168 morfos / 994 entradas / 0 sin value hoy — la puerta se
abre vacía a propósito. morfo 270/270 · server 1850 · client soma 643 ·
check 71 (techo).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
//
// BUT THE SKIP IS TALLIED (2026-08-27): this `continue` used to be
// MUTE — it neither counted nor printed, and the run then reported
// "All N routed morfos validate", a sentence that silently spoke
// only for the parts that happened to mount. That is the house's
// own "a guard that inspects NOTHING passes" class, living inside
// the instrument the house uses to catch it. The summary now says
// how many parts went unseen and — the number that turns a figure
// into a diagnosis — across how many DISTINCT components: few
// components means a demo problem, spread out means the headline
// was speaking for a fraction all along.
unmountedParts += 1 ;
unmountedMorfos . add ( morfo . kebab ) ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
continue ;
}
const declared = new Set ( part . data . map ( ( d ) = > d . attr ) ) ;
declared . add ( partAttr ) ; // the part-identifying attr itself
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
// Sibling parts' identifying attrs are legitimate (Link-is-Item pattern).
for ( const siblingAttr of allPartAttrs ) declared . add ( siblingAttr ) ;
const declaredByAttr = new Map < string , MorfoData > ( part . data . map ( ( d ) = > [ d . attr , d ] ) ) ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
for ( const elAttrs of elementsAttrs ) {
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
// THE EDGE, resolved per element: the ARIA/role that any CO-LOCATED
// part (another morfo sharing this element — or a sibling part of
// this one, the Link-is-Item pattern) declares is OWNED at that
// edge and never host drift.
const edgeAria = new Set < string > ( ) ;
const edgeRoles = new Set < string > ( ) ;
for ( const { name } of elAttrs ) {
if ( name === partAttr ) continue ;
const edge = edgeIndex . get ( name ) ;
if ( ! edge ) continue ;
for ( const entry of edge . part . aria ? ? [ ] ) edgeAria . add ( entry . attr ) ;
if ( edge . part . role ) edgeRoles . add ( edge . part . role ) ;
}
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
// (a) required attrs present
for ( const data of part . data ) {
if ( ( data . severity ? ? 'required' ) !== 'required' ) continue ;
const found = elAttrs . find ( ( a ) = > a . name === data . attr ) ;
if ( ! found ) {
issues . push ( {
kind : 'missing' ,
message : ` ${ morfo . kebab } . ${ part . kebab } : required attr " ${ data . attr } " not emitted `
} ) ;
}
}
// (b) enum-valued attrs have valid values
for ( const { name , value } of elAttrs ) {
const decl = declaredByAttr . get ( name ) ;
if ( decl ? . values && ! decl . values . includes ( value ) ) {
issues . push ( {
kind : 'bad-value' ,
message : ` ${ morfo . kebab } . ${ part . kebab } : " ${ name } " has value " ${ value } ", morfo declares [ ${ decl . values . join ( ', ' ) } ] `
} ) ;
}
}
// (c) no undeclared public data-{component}-* attrs
for ( const { name } of elAttrs ) {
if ( ! name . startsWith ( ` data- ${ morfo . kebab } ` ) && ! name . startsWith ( 'data-' ) ) continue ;
if ( name . startsWith ( ` data-_ ` ) ) continue ; // private escape hatch
if ( declared . has ( name ) ) continue ;
// Eidos-owned cross-cutting hooks (wrapper visual attrs, shared
// styling surfaces) are sanctioned OUTSIDE the morfo — the signed
// wrapper-attrs doctrine. One list, shared with eidos-lint-all,
// so the two instruments agree (P0 fase A, audit 2026-08-26).
if ( EIDOS_ONLY_ATTRS . has ( name ) ) continue ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
// Allow other components' part attrs (nested composition).
if ( ! name . startsWith ( ` data- ${ morfo . kebab } ` ) ) continue ;
issues . push ( {
kind : 'undeclared' ,
message : ` ${ morfo . kebab } . ${ part . kebab } : undeclared attr " ${ name } " emitted on element (not in morfo) `
} ) ;
}
// ── ARIA pass (P1·A11Y, ADVISORY until its first harvest is signed) ──
feat(morfo)!: P1 — split attrs[]/aria[] y vocabulario ARIA cerrado: cada cubo su territorio
El cubo aria[] mentía un 13,5 %: 989 entradas medidas — 847 aria-*, 8
role, y 134 impostores (type ×130, tabindex ×2, for, contenteditable).
- MorfoPart.attrs[]: cubo nuevo para atributos HTML genéricos, misma
forma de entrada y mismo compilado (un solo builder para ambos cubos).
- aria[] se CIERRA por tipo: MorfoAriaAttr = 'role' | unión del spec (53
nombres, DERIVADA del array ARIA_ATTR_VOCABULARY — nunca lista paralela,
la lección MorfoElement). aria-grabbed/aria-dropeffect deprecados-en-1.2
con acta (verdad embarcada de drag-drop); braille*/description son 1.3 y
entran anotados. Un typo muere en el tipo.
- Esquema guarda el territorio en AMBAS direcciones: aria[] con no-aria
LANZA sugiriendo attrs[]; attrs[] con aria-*/role o data-* LANZA.
- Codemod: 134 entradas movidas en 62 morfos (133 bloques). Orden de
emisión MEDIDO y conservado: data → attrs → aria (132/133 bloques
declaraban el impostor como prefijo contiguo de aria[]); snapshot de los
921 parts × 168 morfos: 920/921 byte-idénticos — la única divergencia
(breadcrumb.ellipsis, type tras un aria-label consumerWins) no cambia la
bolsa renderizada.
- Consumidores de part.aria censados: compile/resolver/schema
actualizados; morfo-check pierde un filtro MUERTO (el cubo ya llega
cerrado por tipo+schema); landmark-census y tests de la mitad aria sin
cambio.
- morfo.md documenta los dos cubos. 13 tests nuevos (8 schema territorio +
5 compile paridad). morfo 262/262 · soma entera 1314/1314 · check 71.
Nota de árbol compartido: src/uix/morfo/source-census.test.ts (sin
trackear) es el eje SourceMap de vicen-e1 y NO viaja aquí (add -u); su
censo re-medirá sobre este árbol asentado, orden acordado por canal.
Ejecutado por agente Opus 5, sesión coordinando y verificando (guardas de
territorio probadas en vivo, suite re-corrida).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
// `part.aria` is now CLOSED to `ARIA_ATTR_VOCABULARY` (P1·CONTRATO):
// the 134 plain-HTML entries this pass used to skip by hand
// (`type` × 130, `tabindex` × 2, `for`, `contenteditable`) live in
// `part.attrs` and the schema throws if one comes back. Nothing to
// filter here any more.
// `Set<string>`, not `Set<MorfoAriaAttr>` — it is probed below with
// rendered attribute names, which are plain strings.
feat(morfo)!: decisión 7 — `value` es OPCIONAL en aria[]/attrs[]: declarar la EXISTENCIA sin poseer el valor
Firmada por el autor (bolsa de 12, 2026-08-27). La asimetría con data[] —que
siempre admitió la entrada sin fuente— hacía INEXPRESABLE la cura del lote L5
(un attr cuya aritmética el morfo no puede computar: depth+1, colIndex+1);
un constructor lo midió con tsc y paró. Ahora el contrato puede decir «este
attr existe, el provider posee su valor», que es la forma que data[] practica.
- types.ts: `value?`, con la doctrina en el JSDoc.
- compile.ts: guard `if (!entry.value) continue` en ambos cubos, réplica
LITERAL del de data[] (:452): sin plan y sin deps, pero la entrada consta.
- schema.ts: `value` opcional + invariante nuevo — `ariaBoolean` sin value
LANZA (pedir stringificación booleana de un valor inexistente es
declaración incoherente).
- resolver.ts: consecuencia OBLIGADA que el lote destapó — `resolvePartAttrs`
escribía `undefined` en la bolsa para una entrada sin fuente, y `dom.apply`
lee undefined como BORRA: habría pisado el valor del provider (la ley de
wave por la otra puerta). Ahora salta, simétrico a data.
- morfo-check: el pase ARIA ya distinguía «declarado sin fuente» de «no
declarado» por construcción (declaredAria mapea por attr); queda la ley
escrita y la mitad «required sin condición» SIGUE exigiéndose: que el
provider posea el valor es la promesa de que emite uno.
PROVENIENCIA CORREGIDA en los 3 sitios (JSDoc de schema, JSDoc del flag y el
MENSAJE QUE SE LANZA): citaban la regresión f48acab37 como la clase que el
invariante cierra y NO la cierra — aquella entrada TENÍA value (stateRef, sin
flag), así que retorna antes; la mentira entró por la REGISTRACIÓN, que un
esquema no puede ver porque no ve al provider. Esa clase la cierran la
doctrina de composición y el censo de fuentes. Medido: las 114 apariciones
del flag llevan todas value ⇒ hoy el invariante no muerde ninguna. Higiene
contra una forma, con su proveniencia honesta. (Hallazgo del peer vicen-e1,
verificado por mí con git show.)
Corpus medido: 168 morfos / 994 entradas / 0 sin value hoy — la puerta se
abre vacía a propósito. morfo 270/270 · server 1850 · client soma 643 ·
check 71 (techo).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
//
// DECLARED WITHOUT A SOURCE IS DECLARED (decision 7, 2026-08-27): an
// entry with no `value` hands the value to the provider's bag, so it
// belongs in this set — checks (d) and (f) below key on `attr` only
// and need no `value` branch. A rendered attr it names is contract,
docs(morfo)!: filas 13 y 14 — los TRES estados de una entrada sin fuente, y la regla de alpha deja de vivir solo en prosa
FILA 13(c), firmada: una entrada declarada sin `value` tiene tres estados y
la diferencia era invisible. (1) con value: la emite la bolsa y la verifican
compilador, resolver e instrumento. (2) sin value, required, sin condición:
la emite el PROVIDER y morfo-check EXIGE el attr en el elemento — la única
forma sin fuente que alguien verifica. (3) sin value con optional o con
condición: CONTRATO-ONLY — nadie la emite y nadie la exige; legitima el attr
cuando aparece y documenta que el provider puede emitirlo, nada más.
El tercer estado se QUEDA porque es la única forma de decir «el provider lo
posee Y es condicional» — descartadas (a) exigir siempre (pondría rojo el
caso legítimo) y (b) prohibir la condición (quita expresividad honesta). Se
escribe donde se lee: el JSDoc del campo, el comentario del pase (d) que
prometía más de lo que el código hace, y una tabla en morfo.md. Dato para no
optimizar lo minúsculo: en ese pase `severity` es el filtro DOMINANTE y
`condition` el marginal.
FILA 14(a), firmada: `web/routes/alpha` (TERMINATED) pasa de doctrina en
prosa a entrada de .gitignore con su acta. La lección que la motiva es del
peer vicen-e1: `git add <directorio>` consulta .gitignore, JAMÁS doctrina —
una regla que solo vive en prosa no la hace cumplir nadie, y anoche entró en
un commit por eso. De paso deja de ensuciar el status de las tres sesiones.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
// not drift.
//
// WHAT (d) ACTUALLY DEMANDS, stated exactly (row 13, author-signed
// 2026-08-27 — an earlier draft of this comment promised more than
// the code does): (d) demands emission only for a `required` entry
// with NO condition. So a sourceless entry is verified when it is
// required and unconditional — the provider owning the value is
// then the promise that it emits one — and is CONTRACT-ONLY when it
// carries `severity: 'optional'` or a `condition`: nothing emits it
// and nothing here demands it. That third state is deliberate (it
// is the only way to declare «the provider owns this AND it is
// conditional»), not an oversight, and `severity` is the dominant
// filter of the two.
feat(morfo)!: P1 — split attrs[]/aria[] y vocabulario ARIA cerrado: cada cubo su territorio
El cubo aria[] mentía un 13,5 %: 989 entradas medidas — 847 aria-*, 8
role, y 134 impostores (type ×130, tabindex ×2, for, contenteditable).
- MorfoPart.attrs[]: cubo nuevo para atributos HTML genéricos, misma
forma de entrada y mismo compilado (un solo builder para ambos cubos).
- aria[] se CIERRA por tipo: MorfoAriaAttr = 'role' | unión del spec (53
nombres, DERIVADA del array ARIA_ATTR_VOCABULARY — nunca lista paralela,
la lección MorfoElement). aria-grabbed/aria-dropeffect deprecados-en-1.2
con acta (verdad embarcada de drag-drop); braille*/description son 1.3 y
entran anotados. Un typo muere en el tipo.
- Esquema guarda el territorio en AMBAS direcciones: aria[] con no-aria
LANZA sugiriendo attrs[]; attrs[] con aria-*/role o data-* LANZA.
- Codemod: 134 entradas movidas en 62 morfos (133 bloques). Orden de
emisión MEDIDO y conservado: data → attrs → aria (132/133 bloques
declaraban el impostor como prefijo contiguo de aria[]); snapshot de los
921 parts × 168 morfos: 920/921 byte-idénticos — la única divergencia
(breadcrumb.ellipsis, type tras un aria-label consumerWins) no cambia la
bolsa renderizada.
- Consumidores de part.aria censados: compile/resolver/schema
actualizados; morfo-check pierde un filtro MUERTO (el cubo ya llega
cerrado por tipo+schema); landmark-census y tests de la mitad aria sin
cambio.
- morfo.md documenta los dos cubos. 13 tests nuevos (8 schema territorio +
5 compile paridad). morfo 262/262 · soma entera 1314/1314 · check 71.
Nota de árbol compartido: src/uix/morfo/source-census.test.ts (sin
trackear) es el eje SourceMap de vicen-e1 y NO viaja aquí (add -u); su
censo re-medirá sobre este árbol asentado, orden acordado por canal.
Ejecutado por agente Opus 5, sesión coordinando y verificando (guardas de
territorio probadas en vivo, suite re-corrida).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
const declaredAria = new Set < string > ( ( part . aria ? ? [ ] ) . map ( ( a ) = > a . attr ) ) ;
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
const exceptions = ARIA_PASS_EXCEPTIONS [ ` ${ morfo . kebab } . ${ part . kebab } ` ] ;
// (d) required UNCONDITIONAL aria entries present. Conditioned
// entries are skipped — a static demo page may legitimately not
// satisfy `part-present` / `prop-truthy`, same philosophy as the
// data pass trusting `severity` to encode it.
for ( const entry of part . aria ? ? [ ] ) {
if ( ( entry . severity ? ? 'required' ) !== 'required' ) continue ;
if ( entry . condition ) continue ;
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
if ( exceptions ? . [ entry . attr ] ) continue ;
if ( ! elAttrs . some ( ( a ) = > a . name === entry . attr ) ) {
issues . push ( {
kind : 'missing' ,
message : ` ${ morfo . kebab } . ${ part . kebab } : required aria " ${ entry . attr } " not emitted `
} ) ;
}
}
// (e) the part's declared role must be the rendered role; a rendered
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
// role with no declaration is drift the contract cannot see — unless
// a co-located part owns that role at the edge.
const renderedRole = elAttrs . find ( ( a ) = > a . name === 'role' ) ? . value ;
if ( part . role && renderedRole && renderedRole !== part . role ) {
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
if ( ! edgeRoles . has ( renderedRole ) ) {
issues . push ( {
kind : 'bad-value' ,
message : ` ${ morfo . kebab } . ${ part . kebab } : role " ${ renderedRole } " rendered, morfo declares " ${ part . role } " `
} ) ;
}
} else if ( ! part . role && renderedRole && ! declaredAria . has ( 'role' ) ) {
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
if ( ! edgeRoles . has ( renderedRole ) ) {
issues . push ( {
kind : 'undeclared' ,
message : ` ${ morfo . kebab } . ${ part . kebab } : role " ${ renderedRole } " rendered but the morfo declares none `
} ) ;
}
}
// (f) rendered aria-* the morfo does not declare — except the A-85
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
// consumer-naming class, which the consumer legally owns, and the
// attrs a co-located part declares (owned at the edge).
for ( const { name } of elAttrs ) {
if ( ! name . startsWith ( 'aria-' ) ) continue ;
if ( declaredAria . has ( name ) ) continue ;
if ( CONSUMER_NAMING_ARIA . has ( name ) ) continue ;
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
if ( edgeAria . has ( name ) ) continue ;
if ( exceptions ? . [ name ] ) continue ;
issues . push ( {
kind : 'undeclared' ,
message : ` ${ morfo . kebab } . ${ part . kebab } : undeclared aria attr " ${ name } " emitted (not in morfo) `
} ) ;
}
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
}
return issues ;
}
// ── Main ────────────────────────────────────────────────────────────────────
const BASE = process . argv [ 2 ] ? ? ( await probePort ( 5173 , 5180 ) ) ;
if ( ! BASE ) {
console . error ( 'Could not find a running dev server on 5173-5180.' ) ;
console . error ( 'Start it with `npm run dev` in another terminal.' ) ;
process . exit ( 2 ) ;
}
console . error ( ` Using dev server at ${ BASE } ` ) ;
const morfos = await loadMorfos ( ) ;
if ( morfos . length === 0 ) {
console . error ( 'No morfos found under src/uix/morfo/components/' ) ;
process . exit ( 2 ) ;
}
console . error ( ` Loaded ${ morfos . length } morfo ${ morfos . length === 1 ? '' : 's' } ` ) ;
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
const edgeIndex = buildEdgeIndex ( morfos ) ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const browser = await chromium . launch ( ) ;
const ctx = await browser . newContext ( ) ;
const failures : Array < { morfo : string ; issues : Issue [ ] } > = [ ] ;
const skipped : string [ ] = [ ] ;
let checked = 0 ;
let advisoryCount = 0 ;
const advisoryMorfos = new Set < string > ( ) ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
for ( const morfo of morfos ) {
const route = routeForMorfo ( morfo ) ;
if ( ! route ) {
skipped . push ( morfo . kebab ) ;
console . log ( ` SKIP ${ morfo . kebab . padEnd ( 24 ) } no routed demo under ${ routePrefix } ` ) ;
continue ;
}
checked += 1 ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const page = await ctx . newPage ( ) ;
try {
await page . goto ( BASE + route , { waitUntil : 'networkidle' , timeout : 20000 } ) ;
await page . waitForTimeout ( 500 ) ;
feat(morfo)!: la ARISTA es ley — instrumento composition-aware, residuo a cero y el pase ARIA GATEA
El instrumento de la fila cero, en su forma magra firmada por ambos
carriles: la identidad del compuesto YA viaja por la tubería única (cada
parte compuesta estampa su attr identificador namespaced en el mismo
elemento), así que morfo-check resuelve la arista desde los ATTRS
CO-LOCALIZADOS — la cláusula del pase data («allow other components' part
attrs») generalizada al ARIA que esas partes declaran. Cero superficie
nueva de contrato. Índice de 919 attrs con guardia de colisión ruidosa
(cero hoy, medido). Límites DECLARADOS en el header: ARIA de consumidor
sigue flagged (search-field) · lo que no monta es invisible (clase
RateButton) · dos-contratos-mismo-attr = check futuro nombrado. Disparador
NOMBRADO para el marcador explícito: si el flip bidireccional del censo de
fuentes destapa filas que lo estático no adjudica.
Resultado medido: cosecha 258 → 16 → CERO (la clase de composición cayó
al 100 % por construcción; dos pasadas byte-idénticas + confirmación con
gate armado: 161/161, exit 0).
El residuo, adjudicado leyendo:
- card-group: la causa raíz era UN STRIP — la eidos amputaba
data-collapsible-trigger/content «para que solo pinte Button/grid»
(comentario literal). El attr identificador ES contrato, jamás un toggle
de estilo: RESTAURADO, y el conflicto de pintura se resuelve con la
arista EN CSS — collapsible guarda sus 4 bloques de trigger con
:not([data-button]) (un Button del sistema trae su pintura — §15, el
canal sigue al dueño) y card-group neutraliza el padding con el selector
de DOS attrs [data-collapsible-content][data-card-group-content] (la
arista como selector, especificidad 2). Verificado en vivo: pintura de
Button intacta, padding 0, clip del colapso conservado, ambas aristas
presentes. (El no-toggle con dispatch sintético es limitación de sonda —
el Collapsible plano tampoco; perm-check con clicks reales lo ejercita.)
- gradient-builder.angle-dial: forma de parte corregida — era un WRAPPER
de layout declarando role slider + trío de un elemento que eidos
renderiza como div contenedor del <Slider> compuesto (el Slider posee su
semántica en SUS partes).
- table demo: la celda FALSA deja de reclamar data-table-cell (attr de
parte sin su parte = mentira al instrumento); su pintura prestada queda
re-escrita demo-side.
- Excepciones FIRMADAS ×2 con dueño de la cura: search-field.input ×3
(harness combobox escrito por LA DEMO) · palabras.status aria-live (la
eidos escribe el div a mano por su propia doctrina 4.1.3 — fila del eje
palabras).
FLIP: el pase ARIA deja de ser advisory y GATEA (la ley P0: el gate se
arma DESPUÉS del cero). La maquinaria advisory queda documentada como
patrón de rollout de la casa para el próximo pase.
morfo 264/264 · confirmación gateada 161/161 exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
const allIssues = await validateMorfoAgainstDom ( page , morfo , edgeIndex ) ;
// The aria pass is advisory (see the Issue type): printed and tallied
// apart, never failing the run, until its first harvest is signed.
const issues = allIssues . filter ( ( i ) = > ! i . advisory ) ;
const advisories = allIssues . filter ( ( i ) = > i . advisory ) ;
if ( advisories . length > 0 ) {
advisoryCount += advisories . length ;
advisoryMorfos . add ( morfo . kebab ) ;
}
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
if ( issues . length === 0 ) {
const ariaNote = advisories . length > 0 ? ` (aria advisory: ${ advisories . length } ) ` : '' ;
console . log ( ` PASS ${ morfo . kebab . padEnd ( 24 ) } ${ route } ${ ariaNote } ` ) ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} else {
console . log ( ` FAIL ${ morfo . kebab . padEnd ( 24 ) } ${ route } ` ) ;
issues . forEach ( ( i ) = > console . log ( ` [ ${ i . kind } ] ${ i . message } ` ) ) ;
failures . push ( { morfo : morfo.kebab , issues } ) ;
}
advisories . forEach ( ( i ) = > console . log ( ` [aria-advisory: ${ i . kind } ] ${ i . message } ` ) ) ;
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} catch ( e ) {
console . log ( ` ERROR ${ morfo . kebab . padEnd ( 24 ) } ${ ( e as Error ) . message } ` ) ;
failures . push ( {
morfo : morfo.kebab ,
issues : [ { kind : 'missing' , message : ` navigation error: ${ ( e as Error ) . message } ` } ]
} ) ;
} finally {
await page . close ( ) ;
}
}
await browser . close ( ) ;
console . log ( '' ) ;
feat(morfo)!: decisión 7 — `value` es OPCIONAL en aria[]/attrs[]: declarar la EXISTENCIA sin poseer el valor
Firmada por el autor (bolsa de 12, 2026-08-27). La asimetría con data[] —que
siempre admitió la entrada sin fuente— hacía INEXPRESABLE la cura del lote L5
(un attr cuya aritmética el morfo no puede computar: depth+1, colIndex+1);
un constructor lo midió con tsc y paró. Ahora el contrato puede decir «este
attr existe, el provider posee su valor», que es la forma que data[] practica.
- types.ts: `value?`, con la doctrina en el JSDoc.
- compile.ts: guard `if (!entry.value) continue` en ambos cubos, réplica
LITERAL del de data[] (:452): sin plan y sin deps, pero la entrada consta.
- schema.ts: `value` opcional + invariante nuevo — `ariaBoolean` sin value
LANZA (pedir stringificación booleana de un valor inexistente es
declaración incoherente).
- resolver.ts: consecuencia OBLIGADA que el lote destapó — `resolvePartAttrs`
escribía `undefined` en la bolsa para una entrada sin fuente, y `dom.apply`
lee undefined como BORRA: habría pisado el valor del provider (la ley de
wave por la otra puerta). Ahora salta, simétrico a data.
- morfo-check: el pase ARIA ya distinguía «declarado sin fuente» de «no
declarado» por construcción (declaredAria mapea por attr); queda la ley
escrita y la mitad «required sin condición» SIGUE exigiéndose: que el
provider posea el valor es la promesa de que emite uno.
PROVENIENCIA CORREGIDA en los 3 sitios (JSDoc de schema, JSDoc del flag y el
MENSAJE QUE SE LANZA): citaban la regresión f48acab37 como la clase que el
invariante cierra y NO la cierra — aquella entrada TENÍA value (stateRef, sin
flag), así que retorna antes; la mentira entró por la REGISTRACIÓN, que un
esquema no puede ver porque no ve al provider. Esa clase la cierran la
doctrina de composición y el censo de fuentes. Medido: las 114 apariciones
del flag llevan todas value ⇒ hoy el invariante no muerde ninguna. Higiene
contra una forma, con su proveniencia honesta. (Hallazgo del peer vicen-e1,
verificado por mí con git show.)
Corpus medido: 168 morfos / 994 entradas / 0 sin value hoy — la puerta se
abre vacía a propósito. morfo 270/270 · server 1850 · client soma 643 ·
check 71 (techo).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
// The headline can never speak for what the run could not look at: parts that
// never mounted in their demo are reported BEFORE the verdict, with how many
// distinct components they come from (few = a demo problem; spread out = the
// headline was speaking for a fraction). Printed on every outcome, including
// the green one — that is the whole point.
if ( unmountedParts > 0 ) {
console . log (
` ${ unmountedParts } declared part ${ unmountedParts === 1 ? '' : 's' } never mounted in ${ unmountedMorfos . size } of the checked demos — NOT validated (nothing was looked at, so nothing passed). `
) ;
}
if ( checked === 0 ) {
console . log ( ` No routed morfos found under ${ routePrefix } . ` ) ;
process . exit ( 2 ) ;
} else if ( failures . length === 0 ) {
console . log (
` All ${ checked } routed morfo ${ checked === 1 ? '' : 's' } validate against their demo DOM. `
) ;
if ( advisoryCount > 0 ) {
console . log (
feat(morfo)!: decisión 7 — `value` es OPCIONAL en aria[]/attrs[]: declarar la EXISTENCIA sin poseer el valor
Firmada por el autor (bolsa de 12, 2026-08-27). La asimetría con data[] —que
siempre admitió la entrada sin fuente— hacía INEXPRESABLE la cura del lote L5
(un attr cuya aritmética el morfo no puede computar: depth+1, colIndex+1);
un constructor lo midió con tsc y paró. Ahora el contrato puede decir «este
attr existe, el provider posee su valor», que es la forma que data[] practica.
- types.ts: `value?`, con la doctrina en el JSDoc.
- compile.ts: guard `if (!entry.value) continue` en ambos cubos, réplica
LITERAL del de data[] (:452): sin plan y sin deps, pero la entrada consta.
- schema.ts: `value` opcional + invariante nuevo — `ariaBoolean` sin value
LANZA (pedir stringificación booleana de un valor inexistente es
declaración incoherente).
- resolver.ts: consecuencia OBLIGADA que el lote destapó — `resolvePartAttrs`
escribía `undefined` en la bolsa para una entrada sin fuente, y `dom.apply`
lee undefined como BORRA: habría pisado el valor del provider (la ley de
wave por la otra puerta). Ahora salta, simétrico a data.
- morfo-check: el pase ARIA ya distinguía «declarado sin fuente» de «no
declarado» por construcción (declaredAria mapea por attr); queda la ley
escrita y la mitad «required sin condición» SIGUE exigiéndose: que el
provider posea el valor es la promesa de que emite uno.
PROVENIENCIA CORREGIDA en los 3 sitios (JSDoc de schema, JSDoc del flag y el
MENSAJE QUE SE LANZA): citaban la regresión f48acab37 como la clase que el
invariante cierra y NO la cierra — aquella entrada TENÍA value (stateRef, sin
flag), así que retorna antes; la mentira entró por la REGISTRACIÓN, que un
esquema no puede ver porque no ve al provider. Esa clase la cierran la
doctrina de composición y el censo de fuentes. Medido: las 114 apariciones
del flag llevan todas value ⇒ hoy el invariante no muerde ninguna. Higiene
contra una forma, con su proveniencia honesta. (Hallazgo del peer vicen-e1,
verificado por mí con git show.)
Corpus medido: 168 morfos / 994 entradas / 0 sin value hoy — la puerta se
abre vacía a propósito. morfo 270/270 · server 1850 · client soma 643 ·
check 71 (techo).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
` ARIA advisory: ${ advisoryCount } finding ${ advisoryCount === 1 ? '' : 's' } across ${ advisoryMorfos . size } morfos — adjudicate, then flip the pass to gating. `
) ;
}
if ( skipped . length > 0 ) {
console . log ( ` ${ skipped . length } / ${ morfos . length } morfos skipped without routed demo. ` ) ;
}
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
process . exit ( 0 ) ;
} else {
const totalIssues = failures . reduce ( ( sum , f ) = > sum + f . issues . length , 0 ) ;
console . log (
` ${ failures . length } / ${ checked } routed morfos failed ( ${ totalIssues } total issues): ${ failures . map ( ( f ) = > f . morfo ) . join ( ', ' ) } `
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
) ;
if ( advisoryCount > 0 ) {
console . log (
feat(morfo)!: decisión 7 — `value` es OPCIONAL en aria[]/attrs[]: declarar la EXISTENCIA sin poseer el valor
Firmada por el autor (bolsa de 12, 2026-08-27). La asimetría con data[] —que
siempre admitió la entrada sin fuente— hacía INEXPRESABLE la cura del lote L5
(un attr cuya aritmética el morfo no puede computar: depth+1, colIndex+1);
un constructor lo midió con tsc y paró. Ahora el contrato puede decir «este
attr existe, el provider posee su valor», que es la forma que data[] practica.
- types.ts: `value?`, con la doctrina en el JSDoc.
- compile.ts: guard `if (!entry.value) continue` en ambos cubos, réplica
LITERAL del de data[] (:452): sin plan y sin deps, pero la entrada consta.
- schema.ts: `value` opcional + invariante nuevo — `ariaBoolean` sin value
LANZA (pedir stringificación booleana de un valor inexistente es
declaración incoherente).
- resolver.ts: consecuencia OBLIGADA que el lote destapó — `resolvePartAttrs`
escribía `undefined` en la bolsa para una entrada sin fuente, y `dom.apply`
lee undefined como BORRA: habría pisado el valor del provider (la ley de
wave por la otra puerta). Ahora salta, simétrico a data.
- morfo-check: el pase ARIA ya distinguía «declarado sin fuente» de «no
declarado» por construcción (declaredAria mapea por attr); queda la ley
escrita y la mitad «required sin condición» SIGUE exigiéndose: que el
provider posea el valor es la promesa de que emite uno.
PROVENIENCIA CORREGIDA en los 3 sitios (JSDoc de schema, JSDoc del flag y el
MENSAJE QUE SE LANZA): citaban la regresión f48acab37 como la clase que el
invariante cierra y NO la cierra — aquella entrada TENÍA value (stateRef, sin
flag), así que retorna antes; la mentira entró por la REGISTRACIÓN, que un
esquema no puede ver porque no ve al provider. Esa clase la cierran la
doctrina de composición y el censo de fuentes. Medido: las 114 apariciones
del flag llevan todas value ⇒ hoy el invariante no muerde ninguna. Higiene
contra una forma, con su proveniencia honesta. (Hallazgo del peer vicen-e1,
verificado por mí con git show.)
Corpus medido: 168 morfos / 994 entradas / 0 sin value hoy — la puerta se
abre vacía a propósito. morfo 270/270 · server 1850 · client soma 643 ·
check 71 (techo).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
` ARIA advisory: ${ advisoryCount } finding ${ advisoryCount === 1 ? '' : 's' } across ${ advisoryMorfos . size } morfos. `
) ;
}
if ( skipped . length > 0 ) {
console . log ( ` ${ skipped . length } / ${ morfos . length } morfos skipped without routed demo. ` ) ;
}
morfo: cross-layer contract + 14 new WAI-ARIA components + Dialog wired
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
process . exit ( 1 ) ;
}