Add UIX architecture regression guards

active-uix
dev 5 months ago
parent c13f52ffe9
commit e2ba371beb

@ -29,6 +29,9 @@ Actualizacion 2026-05-14:
- La shell `/uix` ya no carga fuentes remotas; reutiliza las fuentes locales
de Eidos (`Instrument Sans`, `Azeret Mono`) para que las validaciones
Playwright no dependan de red externa.
- `src/uix/contracts.test.ts` ahora incluye guardias de regresion para que
`ActiveUix` no vuelva a bootear Soma/Eidos, los providers Soma no usen
`registerContract/registerPart`, y la shell `/uix` no escriba `prefs.theme`.
- Commits nuevos empujados:
- `694eb5c5` — `Clarify prefs projection contract`
- `223cdf9e` — `Align sema docs with channel ownership`
@ -40,11 +43,13 @@ Actualizacion 2026-05-14:
- `4233be62` — `Update UIX continuation notes`
- `2df58074` — `Use local fonts in UIX docs`
- `8ad04714` — `Refresh UIX architecture docs`
- commit actual — `Add UIX architecture regression guards`
- Validado de nuevo:
- `npx vitest run src/uix/contracts.test.ts src/arts/prefs/test/dom-projection.test.ts src/uix/active-uix/active-uix.svelte.test.ts src/uix/eidos/active-eidos.test.ts src/uix/soma/runtime.svelte.test.ts` -> 79 tests OK.
- `npx vitest run src/uix/sema/engine.test.ts src/uix/soma/runtime.svelte.test.ts` -> 48 tests OK.
- `npx vitest run src/uix/eidos/active-eidos-config.test.ts src/uix/eidos/generated-css.test.ts src/uix/eidos/active-eidos.test.ts src/uix/contracts.test.ts` -> 52 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
- `npx vitest run src/uix/contracts.test.ts` -> 12 tests OK.
- Playwright `/uix`: toggle dark actualiza `html[data-theme="base-dark"]`,
`html[data-mode="dark"]` y mantiene `html[data-density="comfortable"]`;
sin `pageerror`, sin errores de consola y sin `requestfailed`.

@ -1,15 +1,13 @@
// @vitest-environment jsdom
import { existsSync } from 'node:fs';
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { createActiveApp, type ActiveApp } from '$active-app';
import { createActiveDom } from '$adom';
import {
createActivePrefs,
createActivePrefsDomProjection,
standardPrefsDimensions
} from '$prefs';
import { createActivePrefs, createActivePrefsDomProjection, standardPrefsDimensions } from '$prefs';
import type { LangNode } from '$libs/langs';
import { state } from '$libs/reactive';
import {
@ -43,6 +41,9 @@ const prefsProjectionSchema = standardPrefsDimensions({
currency: 'EUR'
}
});
const HERE = dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = join(HERE, '..', '..');
const SOURCE_EXTENSIONS = ['.ts', '.svelte'] as const;
function preferencesHarness() {
return {
@ -53,6 +54,23 @@ function preferencesHarness() {
};
}
function listSourceFiles(root: string): string[] {
const stat = statSync(root);
if (stat.isFile()) {
return SOURCE_EXTENSIONS.some((ext) => root.endsWith(ext)) ? [root] : [];
}
return readdirSync(root)
.flatMap((entry) => listSourceFiles(join(root, entry)))
.filter(
(path) => !path.endsWith('.test.ts') && !path.includes(`${join('components', 'icon', 'lib')}`)
);
}
function grepSources(root: string, pattern: RegExp): string[] {
return listSourceFiles(root).filter((file) => pattern.test(readFileSync(file, 'utf8')));
}
describe('UIX layer contracts', () => {
it('pins ActiveUix public service names and rejects legacy aliases', () => {
const uix = createActiveUix({ langs: minimalLang });
@ -109,6 +127,16 @@ describe('UIX layer contracts', () => {
}
});
it('guards ActiveUix as composition root, not Soma/Eidos bootstrapper', () => {
const source = readFileSync(join(HERE, 'active-uix', 'active-uix.svelte.ts'), 'utf8');
expect(source).not.toMatch(/from ['"].*\/soma/);
expect(source).not.toMatch(/from ['"].*\/eidos/);
expect(source).not.toMatch(/\bSoma\.create\(/);
expect(source).not.toMatch(/\bActiveEidos\.create\(/);
expect(source).not.toMatch(/\bEidos\.create\(/);
});
it('prefs DOM projection owns only cross-modal attrs', () => {
expect(UIX_LAYER_CONTRACTS.prefsDomProjection.implementation).toBe(
'$prefs.createActivePrefsDomProjection'
@ -169,6 +197,14 @@ describe('UIX layer contracts', () => {
}
});
it('guards Soma components from legacy runtime registration APIs', () => {
const violations = grepSources(
join(HERE, 'soma', 'components'),
/registerContract\(|registerPart\(/
);
expect(violations).toEqual([]);
});
it('events/sema uses events as service name and requires dom or projector for visual projection', () => {
const factory = defineEngineSemantic({ visual: false });
expect(factory.name).toBe(UIX_LAYER_CONTRACTS.events.publicServiceName);
@ -207,6 +243,14 @@ describe('UIX layer contracts', () => {
eidos.dispose();
});
it('guards UIX docs shell from writing visual prefs through ActivePrefs', () => {
const violations = grepSources(
join(REPO_ROOT, 'web', 'routes', 'uix'),
/prefs\.theme|setIntent\(['"]theme['"]/
);
expect(violations).toEqual([]);
});
it('Morfo owns translations declaratively and only registers them into a target', () => {
_resetMorfoRegistryForTesting();
const applied: Array<{ namespace: string; module: LangNode }> = [];

Loading…
Cancel
Save

Powered by TurnKey Linux.