fix(theming): S1 de la GRANDE - el prepareWith GRITA y el STALE ve los patrones

Primer barrido de la firma de instrumento grande (cegueras 6 + 1 del
expediente VA). Dos cambios en theming-sentinel.ts:

- Ceguera 6: el catch del bucle prepareWith avisa SIEMPRE por stderr - el
  count() previo ya filtro la ausencia, asi que lo que cae ahi es un control
  que estaba y fallo, la fuente de las muertes fantasma (chat-message midio
  59/81 y 77/81 sobre codigo identico). No lanza: un barrido debe terminar,
  pero una corrida con aviso NO es certificable.
- Ceguera 1: stale/oscillating filtran por reasonFor(k) (exactas Y patrones)
  en vez de k in ledger (solo exactas) - 236 de las 821 ranuras adjudicadas
  (29%) no podian disparar STALE, el silencio que costo 207 claves en B'. El
  mensaje distingue: exacta -> retirar la entrada; patron -> ESTRECHARLO
  (nunca retirarlo: cubre una familia y las demas siguen muertas).

Barrido S1 del ledger ENTERO: 91/91 componentes VERDES, cero unadjudicated,
cero avisos de prepareWith (corrida certificable), 40,8 min. label 1/2 y
switch 27/52 byte a byte identicos a las bases - los cambios no tocan
veredictos, solo el informe.

Cosecha: 18 claves STALE, todas de PATRON, todas de toast - los dos patrones
PLANE_SUPERSEDED ({tono}-{surface|border} y {tono}-accent) adjudicados el
2026-08-24 y dejados rancios ESA MISMA NOCHE por SS12.9 (el plano bajo a
:where() y la receta volvio a ganar; el handoff registra los seis tonos
restaurados). Reproducido 3/3 sobre codigo identico. 18/18 vivas = familias
enteras = ambos patrones RETIRADOS (no estrechados) con acta en el ledger;
toast re-corrido tras la retirada: 72/85, exit 0, 0 STALE. La tercera entrada
de toast (action-hover-bg) es clase tono-en-hover: cosecha de S3, intacta.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
alpha-0.1-background
dev 1 month ago
parent 5fb2db027b
commit 6392d6003f

@ -1588,19 +1588,16 @@ export const SENTINEL_PATTERN_EXCEPTIONS: Array<{
TONE_UNREACHED +
' — the three slots share ONE rule, [data-tags-input-item][data-state=active], and the guard sweeps data-focus / data-invalid / data-disabled for this component, never data-state; `border` reads alive because the CONTROL paints it under data-focus, which IS swept. Measured 2026-08-24 forcing data-state=active on the real tag with the tone stamped: --tags-input-secondary-track repaints it (oklch(0.9913 0.0013 286.38) -> rgb(1, 2, 3)) and --tags-input-secondary-text the ink. The eight `-text` keys carry a SECOND cause on top, and it SURVIVES the aria-selected firma of 2026-08-25: what masks the ink of the highlighted tag is [data-archetype=item][data-highlighted]:not([data-state=checked]):not([data-state=on]) at (0,3,0), which still out-ranks the recipe rule [data-tags-input-item][data-state=active] at (0,2,0). Re-measured on the real tag driven by the KEYBOARD: stripping aria-selected alone changes nothing (the ink stays oklch(0.2435 0 0)); stripping data-highlighted too is what lets the tone through (oklch(0.5168 0.1733 305.88)). The old wording blamed aria-selected through the retired item-fg entry, and that attribution was wrong'
},
{
component: 'toast',
pattern: /^(default|affirm|fulfill|risk|threat|loss)-(surface|border)$/,
reason:
PLANE_SUPERSEDED +
' — the six intents x two slots, 12 of its 85 keys, and this is §12.9 on a PORTALED card: the item carries data-depth=overlay and the plane declares `background` and the `border` shorthand at the same (0,1,0) and later, so the whole per-intent CARD is painted by the plane. Measured 2026-08-24 on the real item with transitions frozen: --toast-risk-surface / -border move nothing (bg stays oklch(0.931 0 0)) while --depth-overlay-surface / -border on the same root repaint it (rgb(4, 5, 6)). Visible consequence, registered in next-features §12.9 and NOT fixed from this axis: a risk toast and a fulfill toast wear the same grey card; only the status disc and the action ink carry the intent'
},
{
component: 'toast',
pattern: /^(default|affirm|fulfill|risk|threat|loss)-accent$/,
reason:
'SUPERSEDED by the depth plane AT REST (the accent is a `border-inline-start` longhand and the plane`s later `border` shorthand overwrites all four sides — §12.9, same measurement as the surface/border pattern), but NOT dead: under `[data-toast-item][data-loading]` the pulse rule is (0,2,0) and its @keyframes outranks every normal declaration. Measured 2026-08-24 over 100 frames of the real pulse: without the write the interpolated border-inline-start-color runs oklab L 0.5032..0.8514; with --toast-default-accent=rgb(1, 2, 3) it runs 0.0823..0.5032 — the token reaches, in the one state where the plane cannot reach it'
},
// RETIRED 2026-08-25 (sweep S1 of the instrument firma): the two
// PLANE_SUPERSEDED patterns of toast — /^(default|affirm|fulfill|risk|threat|
// loss)-(surface|border)$/ and /-accent$/, 18 keys. Both were measured true on
// 2026-08-24 and went stale THE SAME NIGHT: §12.9 dropped the depth plane to
// `:where()` (specificity zero), the per-intent card rules win again, and the
// handoff records the six toast tones restored pixel by pixel. The blind STALE
// check (`k in ledger`, exact-only) could not see keys covered by a PATTERN,
// so they revived in silence — S1's first full pattern census caught them:
// 18/18 alive, reproduced 3/3 on identical code (72/85 each run). Whole
// families alive means both patterns cover nothing: retired, not narrowed.
{
component: 'toast',
pattern: /^(affirm|fulfill|risk|threat|loss)-action-hover-bg$/,

@ -803,8 +803,16 @@ async function main() {
try {
await el.click({ timeout: 1500 });
await page.waitForTimeout(350);
} catch {
/* one layer this route cannot show */
} catch (err) {
// The `count()` above ALREADY filtered the absence, so what lands here
// is never "one layer this route cannot show": the control WAS in the
// document and the click failed anyway (covered, detached, re-rendered
// mid-click). Swallowing it produces PHANTOM DEATHS — chat-message
// measured 59/81 and 77/81 on identical code, 18 tokens whose opt-in
// surface silently never mounted. It must not throw (a sweep has to
// finish), but a run that prints this is NOT CERTIFIABLE: repeat it.
const first = String((err as Error)?.message ?? err).split('\n')[0];
console.warn(`sentinel ${component}: prepareWith FAILED — ${sel} — ${first}`);
}
}
@ -1132,8 +1140,8 @@ async function main() {
const ledger = SENTINEL_EXCEPTIONS[component] ?? {};
const patterns = SENTINEL_PATTERN_EXCEPTIONS.filter((p) => p.component === component);
const reasonFor = (k: string) =>
ledger[k] ?? patterns.find((p) => p.pattern.test(k))?.reason ?? null;
const patternFor = (k: string) => patterns.find((p) => p.pattern.test(k)) ?? null;
const reasonFor = (k: string) => ledger[k] ?? patternFor(k)?.reason ?? null;
const adjudicated = dead.filter((k) => reasonFor(k) !== null);
const unadjudicated = dead.filter((k) => reasonFor(k) === null);
// A reason that opens with OSCILLATES: describes a token measured ALIVE that
@ -1143,16 +1151,28 @@ async function main() {
// the next session deletes a correct adjudication (measured 2026-08-24 on
// emoji-picker.open-trigger-fg, alive 4 of 5 runs on the same code).
const oscillates = (k: string) => (reasonFor(k) ?? '').startsWith('OSCILLATES:');
const stale = live.filter((k) => k in ledger && !oscillates(k));
const oscillating = live.filter((k) => k in ledger && oscillates(k));
// ADJUDICATED, not `k in ledger`: an exception written as a PATTERN covers keys
// the exact map never holds, so a token revived under one could never report
// STALE — 236 of the 821 adjudicated slots (29 %) were invisible to this check,
// and 207 keys came back to life in silence during firma B′. `oscillates()`
// already reads through `reasonFor`, so it generalises to patterns for free.
const stale = live.filter((k) => reasonFor(k) !== null && !oscillates(k));
const oscillating = live.filter((k) => reasonFor(k) !== null && oscillates(k));
console.log(`sentinel ${component}: ${live.length}/${keys.length} tokens move a computed value`);
for (const k of adjudicated) console.log(` adjudicated ${k} — ${reasonFor(k)}`);
for (const k of oscillating)
console.log(` oscillating ${k} — read ALIVE this run; the entry stays (${reasonFor(k)})`);
if (stale.length)
// The two kinds do NOT get the same instruction. An exact entry names one
// token and retiring it is the whole fix; a PATTERN names a FAMILY, and the
// other members are still dead — retiring it would bury them. The precedent is
// written by hand in the ledger (tag-group's `hover-affirm-soft-bg` is spelled
// out OF the pattern because the stage reaches that one alone).
for (const k of stale)
console.log(
` STALE exception(s) — the token moves now, the ledger entry should go: ${stale.join(', ')}`
k in ledger
? ` STALE exception ${k} — the token moves now, retire the exact entry (${ledger[k]})`
: ` STALE pattern ${k} — the token moves now; NARROW the pattern ${patternFor(k)!.pattern.source} so it stops covering this key — do NOT retire it, the rest of the family is still dead (${patternFor(k)!.reason})`
);
if (unadjudicated.length)
console.log(

Loading…
Cancel
Save

Powered by TurnKey Linux.