Password is the one field with content BELOW the control (the strength meter), so
unlike the collapsed text fields it needs the full canonical Field structure:
provider STACK > control BOX + supporting content. Added a `<PasswordField.Control>`
part (morfo + soma provider class + component + exports/types + eidos wrapper/index)
that carries `fieldRole('control')`; the Provider takes `fieldRole('provider')` (the
stack) and the input `fieldRole('input')`.
- field.css owns the control box chrome (border/radius/bg/height/padding/focus-within/
invalid/disabled) + the input reset. password-field.css keeps ONLY the affixes:
visibility trigger, caps-lock indicator, strength meter, and the input's mask-tracking.
- REMOVED the 2-row grid + `::before` chrome hack (it existed only to avoid `:has()`
layout flicker without a control element). Now the strength meter is a plain stack
sibling BELOW the control — appears/disappears with no `:has()`, no flicker.
- No tokens aliasing the Field base.
This is the canonical composition (why A over the de-alias compromise: composability
IS the S6 canon; the "API change" — wrapping row 1 in <Control> — aligns password to
how Field itself works, it doesn't invent API). Contained: the demo is the consumer,
updated here.
Baseline unchanged (59). Verified: Provider stack (flex column), Control box paints
from field.css (36px/border/pad/radius), input + eye affix in a row, strength meter
8px below as a sibling, :focus-within ring. Closes S6 field composition — all 16
fields now compose field.css.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Converts the shared `spin-field.css` layer (NumberField + CssField) to the
collapsed text-field pattern. Both morfos add `fieldRole('provider')` +
`fieldRole('control')` to the spin-field box (data-field + data-field-control
alongside the existing data-spin-field marker) and `fieldRole('input')` to the
bare input; both eidos wrappers import field.css.
spin-field.css drops its ENTIRE parallel control chrome (border/radius/bg/height/
padding/font/focus/invalid/disabled/variant/readonly + all `--spin-field-*` chrome
tokens) — field.css `[data-field-control]` owns it now. It keeps ONLY the stepper
affordances (increment/decrement triggers, scrubber, split + stacked layouts,
themeable glyphs), re-sourced off field's OWN resolved control tokens
(`--_field-control-height` / `--_field-control-padding-inline` / `--_field-control-gap`)
so the steppers always match the input row — no parallel size axis. Two box overrides
survive at `[data-spin-field][data-field-control]` (0,2,0, beats field.css regardless
of the global-vs-per-mount load order): `overflow: hidden` (edge steppers break out
of the padding then clip to the corners) and `gap: 0` (steppers butt flush via dividers).
Baseline unchanged (59). Verified in browser: number-field + css-field paint from
field.css (36px/border/radius/overflow), steppers 36px flush at the edges with
dividers, split layout intact.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same collapsed text-field pattern as the search-field pilot: mask-field is a single
inline-flex row box (input only, no affixes), so its Provider part collapses
`fieldRole('provider')` + `fieldRole('control')` and the `<input>` gets
`fieldRole('input')`. field.css owns the entire box chrome (border/radius/bg/height/
padding/hover/focus/invalid/disabled/variant/size) + the bare-input reset; mask-field.css
drops from 143 lines to a single rule — the input's `tabular-nums` (masked patterns are
digit-heavy, keep glyph columns aligned so fixed literals don't shift as the mask fills).
Baseline unchanged (59). Verified: box paints from field.css (36px/border/pad/radius,
flex row), input reset + tabular figures.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pilot for the TEXT-field pattern (distinct from the segmented fields): a text field
is a single inline-flex ROW box (input + affixes), with no label/segment stack — it
IS-A control. So its morfo collapses BOTH Field roles onto the one box element:
`fieldRole('provider')` + `fieldRole('control')` (data-field + data-field-control),
and the `<input>` gets `fieldRole('input')`.
- field.css owns the box chrome (border/radius/bg/height/padding/gap), the input
reset, focus (`:focus-within`), invalid and disabled. search-field.css keeps ONLY
the search affixes (leading icon, loading spinner, clear-trigger empty-state) and
the native `<input type=search>` clear-button suppression. No tokens aliasing Field.
- field.css `[data-field-control]` now sets `flex-direction: row` explicitly: on a
collapsed box the provider's `flex-direction: column` (label/control stack) leaked
in and stacked the affixes vertically. A control is always a prefix/input/suffix
row, so this is a no-op for non-collapsed controls (verified date-field unaffected).
Baseline unchanged (59). Verified: box paints from field.css (36px/border/pad/radius),
input reset, `:focus-within` ring, icon + clear in a row.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Applies the field-composition pattern to color-field (the last segmented field):
- color-field: chrome / label / invalid / disabled / focus come from field.css via
the fieldRole identity (data-field / data-field-control); color-field.css keeps
only its GENUINE specifics — the channel segments (3ch / hex 6ch), the vertical
divider separators, the leading value swatch, the format <Select>, and the
`gap: 0` control override (per-element spacing, so a flex gap would double the
dividers). No tokens aliasing the Field base. Imports field.css.
- ColorPicker: the in-popover value row IS a composed ColorField (it already renders
a ColorField provider element carrying data-field + threaded data-size), so it
only needed field.css imported for the chrome to resolve inside the portal.
Baseline unchanged (59). Verified in browser: color-field paint + label + swatch
(18px) + format select + 5 segments; picker value row paints with field chrome.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Applies the proven date-* pattern to the time family (exact structural parallel):
- time-field: chrome / label / invalid / disabled / focus come from field.css via
the fieldRole identity (data-field / data-field-control); time-field.css keeps
only segment specifics. No tokens aliasing the Field base. Imports field.css.
- time-range-field: COMPOSES two real <TimeField>s (soma TimeRangeField.Input →
TimeField.Provider > TimeField.Input per endpoint) bound to the range slice;
size/variant/color thread through a visual context; CSS is layout-only. (No
syncPlaceholderToValue opt-out needed — TimeField has no calendar to auto-page,
and the controlled placeholder keeps the endpoints from mutating each other.)
- TimePicker (single field): root carries data-field/data-time-field (A27);
imports field.css.
- TimeRangePicker: inherits the composed endpoints; imports field.css (no
double-box — its recipe never re-declared the endpoint box).
Baseline unchanged (59). Verified in browser: time-field paint + label, range
endpoints = composed TimeFields (nested provider>control, size threaded, value
renders), both pickers paint, no double-box.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the two remaining date-range-picker cleanups after the composition refactor:
- Size threading (functional): the eidos `<DateRangePicker.Input>` now reads the
picker's visual context and forwards data-size/variant/color to soma's Input
(= the composed DateRangeField.Input), so each endpoint's composed <DateField>
self-themes at the picker size. Verified reactive (md → lg resizes both endpoints).
- De-alias / prune (no-token-alias hygiene): removed the orphaned
`--_date-field-padding-inline` / `--_date-field-segment-padding-inline`
re-declarations (their consumers — the endpoint box + segment rules — are gone);
reduced the shared label rule to layout-only (colour/size come from
date-range-field.css); RETARGETED the endpoint-invalid rule from the now-borderless
endpoint wrapper to each inner `[data-field-control]` with Field's
`--field-control-border-invalid` (it was a no-op post-composition — invalid now
paints again); and re-based the embedded trigger on the Field `--field-segment-height`
token + shared segment-active tokens + foundation radius (no date-field aliases).
Residual (coupled to the pending date-field recipe-token pruning): the UNUSED
standalone trigger + the root wrap-stack still reference date-field recipe tokens
(height/gap/font-size/accent); they resolve correctly and retire with that pass.
Baseline unchanged (59). Verified: two boxes, embedded trigger sized/rounded,
invalid paints, size reactive.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Design decision (2026-07-08): the range picker keeps TWO separate composed
<DateField> boxes side by side — the border stays on each DateField, never on a
wrapper enclosing both. This overrides the reference-frameworks default (a
web-sourced sweep of React Aria / MUI X / Ark / Mantine / Bits UI found single-box
is the norm 5/5), which is the user's call.
The former `[data-date-range-field-group]` treatment moved the chrome onto a
consumer-supplied wrapper div to collapse the two endpoints into one box. It was
CSS-only, unused (no component part, no demo), and — after the composition
refactor moved each endpoint's box to its inner [data-field-control] — it targeted
the wrong element anyway. Removed outright. Verified: two endpoint boxes intact,
no enclosing box.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The range picker's segments are composed DateField segments (they carry
data-date-field-segment + the shared data-field-segment marker and read their
tokens from each endpoint's own [data-date-field]). The per-picker copy only
duplicated the Field base and re-added a focus font-weight the shared
field-segment-state layer deliberately drops (bold widens the ch-based segment,
jittering it on every increment). Removing it defers to date-field.css +
field-segment-state.css. Verified: segment box / padding / min-width / highlight
unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The date-* family now IS-a-Field then evolves, per the S6 mandate + the
no-token-alias doctrine — no more marker-overlay borrowing of date-field.css.
- date-field: chrome / label / invalid / disabled / focus come from field.css via
the fieldRole identity (data-field / data-field-control); date-field.css keeps
only segment specifics. No tokens aliasing the Field base.
- Field recipe: add --field-segment-height (size-axis, family level).
- DateField.Provider: expose syncPlaceholderToValue for controlled composition.
- date-range-field: COMPOSES two real <DateField>s (DateField.Provider >
DateField.Input per endpoint) bound to the range slice; size/variant/color thread
through a visual context; CSS is layout-only. Fixes the field.css-not-loaded
chrome regression.
- DatePicker (single field): root carries data-field (A27); imports field.css;
drops the dead date-field token re-declarations.
- DateRangePicker: inherits the composed endpoints; imports field.css; removes the
endpoint box that double-boxed the composed DateField.
Baseline unchanged (59). Verified in browser: paint, edit coordination, size
reactivity, no double-box.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow-up to the FieldLangs component: export it from the soma components
barrel and add its demo route to the docs sidebar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
FieldLangs holds one value per language and switches the active editing language
through an adaptive in-control switcher, composing the shipped Field / Popover /
TextArea instead of reinventing them (picker composition pattern, A27).
- Coordinator (soma): values map + activeLang + required-per-language validity;
exposes the value/selection proxies the composed controls bind to; fires
shift-navigate (context change) as its only semantic event.
- Switcher (eidos, FieldLangs' own -- deliberately NOT a composed ToggleGroup,
whose commit-toggle is the wrong semantic for a language navigation and would
leak into the field): a segmented radiogroup for 3 or fewer languages, an own
Popover dropdown for more; per-language set/required dot; roving keyboard, RTL.
- Single-line Input and multiline Textarea (corner switcher, the field's default
floating label untouched); floatingLabel passthrough; switcher font one
typographic step below the control (label doctrine).
- morfo (provider/switcher/lang-option/menu/textarea parts + shift-navigate
event), i18n catalog, and an interactive demo.
Guards: svelte-check clean, morfo test, eidos-lint 0 invalid, component:audit PASS.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the static `lazy` boolean with `showMask`, defaulting to `'focus'`.
A reference sweep (imask `lazy`, jQuery-inputmask `showMaskOnFocus`,
react-input-mask `alwaysShowMask`, ngx-mask `showMaskTyped`, PrimeReact) found
no library that shows the full skeleton on a blurred, empty field by default:
the ones that reveal it do so on focus. So the resting field stays clean, and
focusing reveals `(___) ___-____` with the caret on the first slot; blurring an
unfinished field drops the placeholder tail. `'always'` (skeleton even when
blurred) and `'never'` (progressive) are the opt-outs.
- provider: `effectiveLazy` derived from `showMask` + focus; `revealOnFocus`
(wires the already-exported `firstEditableCaret`) and `settleOnBlur`; `isEmpty`
now reads the unmasked value so a visible skeleton doesn't read as filled.
- `'focus'` composes with `Field.floatingLabel`: focus floats the label and
reveals the skeleton without the two competing for the empty control.
- demo: `showMask` chips replace the `lazy` switch; README documents the
decision + the reference sweep.
Verified all three modes in the browser; provider tests 31 pass (adds a
focus/blur reveal test); check clean for mask-field.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The MaskField demo showed the composed `<Field floatingLabel>` case only as a
static example. Turn it into a toggle in the Field-integration section (default
on) so it reads as a real option, with a note that floatingLabel is a Field-
level mode (not a MaskField prop) that works because MaskField renders a real
<input>. Verified: toggling flips data-floating-label on the wrapping field.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The floated-label fill test targeted `[data-field-input]` — the base
Field.Input only — so `<Field floatingLabel>` didn't float when wrapping a
specialized field (MaskField/NumberField/PasswordField/… render their own
`<input>`). Widen it to any non-hidden text input / textarea inside:
`:has(:is(input, textarea):not([type='hidden']):not(:placeholder-shown))`.
Now the floating label works for any composed text field (the vertical anchor
already aligns via shared --size-*-control-height tokens). Segmented controls
(Date/Time, contenteditable) still float on focus only — deferred.
Demo: MaskField floating example added to its Field-integration section.
Verified in browser: resting placeholder centred, floats onto the border when
filled, mask still conforms the value.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Floating label (`<Field floatingLabel>`), the label typography doctrine, and
its propagation across the text-field family.
Floating label (Material outlined, text inputs only):
- CSS-only via :focus-within + :placeholder-shown — no new soma state. The
label starts as a placeholder inside the control and floats onto the top
border when focused/filled. Reuses Field.Label + Field.Input. Requires the
input to carry `placeholder=" "`. Segmented controls (Date/Time) not covered.
- Floated position is themeable via `--field-floating-label-top` (default 0 =
on the border) without touching the recipe.
Label weight doctrine ("label one step below the component"):
- The field VALUE is now `medium` and the label stays `regular` — one weight
step below (the size offset was already derived one step down). Base Field via
`--field-control-font-weight`; propagated to the specialized text fields
(number/css via spin-field, mask, password, search, textarea, date, time,
date-range, time-range, color) so every field value reads at medium.
Verified: recipe-css-contract + component-visual-attrs + api-contract (30),
eidos-lint 0 invalid, smoke, check 0 field errors. Browser: floating resting↔
floated transitions correctly; value 500 / label 400 across fields.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Native <label> accessible primitive with `for` association — the gap the
Field family left open (Field.Label is context-bound; nothing labelled a
control outside a Field). Ported from morfo-runtime:terra/label into the
current eidos-native leaf shape.
- Eidos-native leaf (scope: ['eidos'], no soma/sema): morfo is the contract
(one Provider part -> data-label); the wrapper renders <label> directly.
- Visual axes size/weight/color follow the Text typography mechanism
(--style-label-* base + inline --_label-* overrides via ActiveEidos.resolve),
not a prop-less shell — real selectable axes in the demo.
- Multi-click mousedown guard (Radix convention) inline in the wrapper;
composes the consumer's onmousedown.
- Complements Field.Label (auto-wired inside a Field); floating label is a
deferred Field concern, not a prop here.
Passes component:audit label (0 err / 0 warn), check (0 label errors),
eidos-lint (0 invalid), recipe-css-contract, smoke. Browser-verified: size/
weight/color change the label live; for-association focuses the control.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two behavior-preserving fixes that unblocked the repo-wide morfo tooling
(morfo:check / morfo:vocabulary / perm:check all threw at loadMorfos):
- data-intent is enum-valued, so the compiler writes it in `enum` mode and
`emit` is ignored; its presence on an enum is rejected by the schema
invariant (schema.ts, tested). Removed it — matches timeline / toast.
- Knob declares 5 events and ships a sema pack (knobSema) but scope was
[soma, eidos]; added 'sema' so declaration matches reality.
Verified: morfo:vocabulary exit 0, schema.test 14/14, morfo:check knob PASS.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rebuilds the GradientPicker on the generic Picker.Provider<Gradient>
transaction core (open / commit / cancel / clear / workingValue + a composed
Popover), like the natural-time-picker — the bespoke coordinator is now a thin
domain provider (morfo runtime + working-value CSS + the presets state).
- deferValue prop (live vs deferred draft-until-Listo). The trigger chip
reflects the COMMITTED value, so a deferred picker's chip stays put while you
edit the working draft.
- Clear restores the INITIAL bound value the picker was given, not a hardcoded
default gradient.
- No tabs: the editor IS the popover body; the presets grid opens in a panel
from a Layers button next to "Añadir degradado" (the Add <-> Remove toggle) —
ColorPicker-style "select a saved preset -> Remove", and it reclaims the
tab-header height.
- Add / Remove presets fire commit.fulfill / commit.loss firmas (new
gradient-picker sema pack, registered in the demo). Per the Button doctrine the
press is a neutral contact.activate and the consumer emits the RESULT commit;
the events target the REGISTERED `provider` part (targeting the unregistered
`content` part was also why the Clear commit-reset was silent).
- Stop color editor hides the remove-stop button for the endpoint stops
(first / last by position — they anchor 0% / 100%).
- maxPresets cap (default 10). StopList in a ScrollArea (autosize, slim
scrollbar with a gap so the rows never sit under it).
Composes the existing components AS-IS (Popover / Button / ScrollArea / the
GradientBuilder parts) without overriding their styles.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GradientBuilder (this session):
- Stop color editor uses the canonical ColorField value row (swatch +
styled Select), not a native select; rails at 90% width + xl track.
- Composable footer: ColorPicker.Cancel / ColorPicker.Close + trash
remove-stop, as a sibling of PickerShell.Body.
- Surface-agnostic by default (variant='ghost'): no card, adapts to host.
- '+' add-stop on the kind-switch row; inserts between the selected stop
and the next; rail click-to-add re-enabled (insert at cursor).
- GradientBuilder.StopList composable stops list (rendered by default).
- presets prop (Gradient[]) rendered below the editor (flex, not tabs);
save-to-presets with a maxPresets cap; the action button flips
Save <-> Delete when a saved preset is selected (ColorPicker mechanic).
Also lands the in-progress gradient tree so HEAD is self-consistent
(langs/components/index.ts already imported ./gradient-picker, whose file
was untracked). gradient-builder + gradient-picker stay on ACTIVE_DEV_TRACK;
the GradientPicker 2-tab redesign is next (see gradient-builder/CONTINUE.md).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mark the workstream done in the continuation handoff: orca translated (A2),
active-app promoted to an architecture chapter (B1), historical DESIGN docs
moved to decisions/ (B2), runtime-artifact shortcut rerouted (B3). Guards green
at close (arts:check 0/0/22, docs:check 0 err).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "I want to… use a runtime artifact" row in docs/README.md now routes
through the composition root (architecture/active-app.md) → the arts map
(arts/README.md) → the per-artifact README. Map/aliases were already complete
(9e48b80b). docs:check 0 errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the three superseded arts design-phase records into the E3 corpus
(already indexed in docs/decisions.md), verbatim-as-historical:
- docs/decisions/design-connection.md (from src/arts/connection/DESIGN_CONN.md)
- docs/decisions/design-session.md (from src/arts/session/DESIGN.md)
- docs/decisions/design-timer.md (from src/arts/timer/DESIGN_TIMR.md, kept Spanish)
Each gets a `status: historical` frontmatter block; the current reference for
each subsystem is its README.md. A stub at every old src/arts/* path keeps the
~30 source provenance citations (`DESIGN_TIMR §12.8`, `DESIGN.md §7.Q1`, …)
resolving. Bodies kept verbatim (no reformat). docs/decisions.md index + naming
note updated to the new paths. docs:check 0 errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ValueField: optionally-editable centre value composing NumberField, wired to
the knob's value (time-picker-hour-slider pattern); de-chromed to read as
text, width-locked to the max value's digit count, and its pointer/keyboard
events isolated from the rotate gesture so editing never rotates or resets
the dial.
- handle-drag: continuous rAF-throttled perceptual tick during rotation,
mirroring the Slider (morfo event + provider emit + sema rule).
- NumberField: first-class maxLength prop (native maxlength, unlimited when
unset); the knob caps the field to the max value's digit count.
- Rotation: publish --knob-start-angle / --knob-sweep so the arc reacts to the
sweep; snap to the nearest end inside the bottom dead zone so overshooting the
max lands on the max and never wraps around to the min.
- Register knobSema in the uix demo layout so the knob emits sound + haptic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the active-app reference into the book tree as the arts composition root,
the App-side analogue of active-uix.md:
- new docs/architecture/active-app.md (frontmatter + fixed relative paths); the
dated Spanish "Handoff 2026-05-13" heading becomes a timeless "Quick start".
- extract that Handoff block verbatim (ES) to docs/process/handoffs-2026-05.md,
alongside the other extracted arts/uix handoffs.
- stub src/arts/active-app/README.md pointing at the chapter (active-uix pattern).
- wire the chapter into the docs/README.md E1 map.
docs:check 0 errors (11 pre-existing warnings unrelated).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Translate the last Spanish README in the arts reconciliation (~1457 lines).
Prose only; all code blocks, ORCA_* constants, type names and the A1 API
corrections that landed in 9e48b80b are preserved verbatim. arts:check 0/0/22.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full Spanish -> English translation of sium/README.md (862 lines; faithful).
Code blocks, schema examples, issue codes and `#?sium.errors.*` idlangrefs kept
verbatim. Also fixed a stale ref: `/test/sium` -> `/active/docs/sium`. Carries
the A1 additions (cssLength/cssValue in Included Refines).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full Spanish -> English translation of cache/README.md (faithful; code blocks,
CACHE_* constants and scope/policy values kept verbatim). The mixed file's
Spanish sections (What It Solves, Keys, Scopes, Policies, Modes, Tags, Mutate,
Explain, Adapters, Events/Logger, Security, Roadmap) translated; the already-
English "Reacting to identity" section left as-is. Carries the A1 fixes
(defineActiveCache signature, ActiveEngine contract surface, demo URL).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full Spanish -> English translation of adom/README.md (faithful; code blocks and
the ActiveDom interface kept verbatim, incl. the Backlog chronicle entries).
Carries the A1 additions (prefersReducedMotion/writeProperty/removeProperty,
listen 4 overloads, standalone rune-helpers table, /active/docs/adom).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full Spanish -> English translation of connection/README.md (faithful; code
blocks, event/error constants and state diagrams kept verbatim). Carries the A1
fixes (required `{ timers }`, the 13 ActiveConnections getters, `/active/docs/conn`
demo, the no-own-scheduler paragraph).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full Spanish -> English translation of auth/README.md (faithful; code blocks,
route/error constants and the `$lib/server/*` SvelteKit-consumer imports kept
verbatim). Example Svelte UI strings translated (Validating.../Hello/Anonymous
session). Also fixed a stale ref: `/test/auth` -> `/active/docs/auth`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full Spanish -> English translation of format/README.md (faithful). Code blocks,
tables and es-ES formatter OUTPUT strings in comments (e.g. "hace 2 horas",
"lunes, 25 de mayo") kept verbatim — they demonstrate locale output, they are
data, not prose. Carries the A1 fixes (units/ path, formatAs/convertAs,
convertToDefault, $format/currency alias).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full Spanish -> English translation of prefs/README.md (faithful; all code /
text blocks and the `prefs::*` error strings kept verbatim). Carries the A1
signature fixes already landed (applyBrowserEnvironment/watchBrowserEnvironment,
createPrefsStorageBridge `engine`).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First A2 batch (Spanish -> English, faithful; code blocks + i18n example data
left untouched):
- clipboard: full translation (was fully Spanish).
- langs: light pass — the "Handoff 2026-05-12" Spanish paragraph only (the rest
was already English; example i18n records like 'Aceptar' are data, not prose).
- logger: light pass — the "Framework Logger Contract" + "Framework Diagnostics"
Spanish spans (rest already English).
No residual Spanish prose in the three files (verified by grep). arts:check 0/0/22.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Second A1 batch — every fix verified against the cited code symbol, not the
plan (the plan repeatedly under- or mis-counted; verifying against code caught
both extra drift and already-fixed items):
- format: `unts/`->`units/` path typo; documented currency `formatAs`/`convertAs`
and units `convertToDefault`; fixed the `$formats/currency` alias (plural, no
such alias) -> `$format/currency` in format + currency READMEs.
- timer: FS diagram completed (11 -> the full 16 source files), "50 tests" -> 47,
reschedule behaviour/errors reconciled (from a prior uncommitted session; all
verified accurate against timer-handle.ts).
- bus: Layer-split diagram corrected (consts/errors/matching/diagnostics/contracts
live in libs/bus, not arts/bus; only EngineBus/EngineBusOptions + engine/svelte
in arts/bus; added silent-bus.ts); documented all 3 listener error modes
(log-and-continue/throw/collect); fixed stale preset names -> the real
cache-clear-on-*/connections-*/perm-invalidate-*/session-auto-refresh/standard.
- connection: `createEngineConnections()` -> `{ timers }` (required); documented
the 6 missing ActiveConnections getters (13-getter surface); conn-chat demo
(nonexistent script + npm task) -> real `/active/docs/conn`; fixed a paragraph
that contradicted the required-timers reality.
- sium: documented `cssLength`/`cssValue`/`CSS_LENGTH_REGEX` refines (were absent);
verified the "codes" item was already covered (siumLangs has all 26).
- prefs: `createPrefsStorageBridge({ prefs, storage, key })` -> `{ engine,
storage, onError?, skipHydrate? }` (prefs->engine, key was phantom);
`applyBrowserEnvironment(prefs,...)` -> `(engine,...)`;
`watchBrowserEnvironment(prefs,...)` -> `(apply,...)` (first arg is a callback).
- http: `DEFAULT_RETRY`/`DEFAULT_TIMEOUT` -> `HTTP_DEFAULT_*`.
- motion: documented the missing `resolve`/`has`/`list`/`exit`; noted `JsDriver
'svelte'` is reserved/unimplemented (README + engine-motion.ts JSDoc, which
falsely listed it as running).
- color: types.ts JSDoc `ActiveEidos.setCssVariables` -> `applyColorScheme`; bare
`THEMING.md` ref -> `../../uix/eidos/THEMING.md`.
- systemic (not in the plan): `defineActive*`/`defineEngine*` imported from
`$active-app/services` (the schema-contract module, which does NOT export them)
-> `$active-app/service-factories`, in clipboard + auth + storage + active-app.
arts:check: 0 errors, 0 warnings, 22 arts. Note: bus/connection/format/prefs/sium/
timer carried prior uncommitted arts-reconciliation edits from an earlier session,
folded in here (all coherent, same initiative).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the provider→recipe direction of the CSS-var drift stumble the
architecture-faithful way: the morfo is the source of truth. A component now
declares the functional custom properties its soma provider writes and its eidos
recipe reads:
cssVars: [{ name: 'progress', … }, { name: 'angle', … }] // → --knob-progress, --knob-angle
- types: `MorfoCssVar` + optional `cssVars` on `Morfo` (additive; the 133
existing morfos are unaffected).
- schema: validates `cssVars` (array of `{ name, description? }`).
- compile: exposes `contracts.cssVars` as full names `--{kebab}-{name}`.
- knob morfo declares its two contract vars.
- guard (recipe-css-contract.test.ts): enumerates morfos via `import.meta.glob`
and verifies BOTH sides honour each declaration — the provider writes
`--{kebab}-{name}` AND the recipe reads it — so renaming one side without the
other fails loudly. Proven non-vacuous (a bogus cssVar is flagged on both
sides).
Why A1 and not a grep cross-check (A2): in the CSS a provider-written functional
var and a consumer-override alias are indistinguishable, and providers publish
hook vars the recipe doesn't consume — a grep gives ~17 false positives (proven
earlier). The morfo declaration is what disambiguates.
npm run check 59 (baseline, 0 in touched files); eidos recipe-css-contract 24/24;
morfo suite green (only the pre-existing dialog-role test fails); docs:check 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Empirically confirmed the grep approach can't distinguish a provider-written
functional var from a consumer-override alias (identical in CSS), and providers
publish API-hook vars the recipe doesn't consume (17 such, none read by var()).
Only A1 (morfo cssVars declaration) can close the provider→recipe direction.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the recipe→theme direction of the soma→eidos CSS-var drift stumble. New
check in recipe-css-contract.test.ts: every NO-FALLBACK `var(--x)` in a
component recipe must resolve to a token declared somewhere in the eidos CSS
tree (foundation + shared layers + the component's own self-declarations). A
`var(--x, default)` stays exempt — those are runtime-optional by construction
(provider/floating vars whose fallback IS the default), matching the existing
`collectOwnPublicVariables` rule.
This is the exact class that silently broke the Knob (`--color-neutral-content`,
`--state-hover`, `--color-neutral-bg` resolved to nothing, only visible in the
render). The guard immediately caught 3 MORE real phantoms — fixed, not silenced
(silencing would be the anti-pattern of a guard exempting the gap it exists to
catch):
- card-group: `--color-content-default` → `--color-content-primary`
- link-preview: `--leading-body` → `--leading-normal`
- textarea: `--font-family-body` → `--font-family-primary`
Full eidos suite 290/290. The provider→recipe direction (renaming a
provider-written `--knob-angle` breaking the recipe) remains open as option A
(morfo `cssVars` declaration or a grep cross-check) — noted in STUMBLES.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ethereal (was undocumented — the miss that prompted this pass):
- new src/arts/ethereal/README.md, API drawn from the code (computePosition,
the 7 middleware, autoUpdate, selectPositioningStrategy, dual JS/CSS-anchor
engine, +1-frame read-phase model)
- wired into src/arts/README.md Map + aliases + cross-deps; $ethereal added to
the CLAUDE.md alias table; the also-missing bus/orca/prefs aliases + Map rows
completed against vite.config.ts
A1 drift (every fix verified against the cited code symbol, not the plan):
- orca: ~13 phantom identifiers across 7 sections (priority, execution /
ORCA_EXEC_*, tokenTimeoutMs, onFatal / ORCA_ON_FATAL_*, onTimeout, the whole
multi-level timeout model, the transaction-port model + ORCA_TX_*,
OrcaMaybePromise, OrcaCompensationContext) reconciled to the real API
(parallel waves, actionTimeoutMs-only, transaction:string tag + compensate,
fatal via returning OrcaFatal); added the real missing fields fanIn / parallel
/ compensate; glossary OrcaRun -> OrcaRunResult
- cache: residual createActiveApp({cache}) -> services:{cache:defineActiveCache()};
documented the ActiveEngine contract surface (loading / lastError / disposed /
snapshot / clearError / onChange / dispose); dangling demo URL ->
/active/get-started/ecosystem
- adom: added prefersReducedMotion / writeProperty / removeProperty; listen
1 -> 4 typed overloads; documented 12 undocumented standalone rune helpers;
/test/adom -> /active/docs/adom
Guard (scripts/arts-check.ts): removed the ethereal README exemption (set empty)
and promoted A-index from warn to error — the net no longer passes green with a
real gap. arts:check: 0 errors, 0 warnings, 22 arts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a '### Authoring notes' subsection to soma.md §6 grounded in the real
defs — state<T>() vs $state, role optionality on a Provider, Without<>/
PrimitiveDivAttributes, and gesture pointermove/up ownership. Closes STUMBLES
#9; only #7 (soma->eidos CSS-var contract, design-open) remains.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Knob now built across all 5 layers, validating the exercise. #7 confirmed
first-hand: the Knob build hit phantom theme tokens + uncontracted provider
CSS vars (silent drift), exactly as predicted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The relocated Knob didn't render (only the number showed). Root causes, all
found by inspecting the loaded stylesheet (not guessing):
- A stray `*/` inside the header comment (`--scale-*/--primitive-*`) closed the
comment early and corrupted the FIRST rule, so the base `[data-knob]` rule
(which sets `--_knob-diameter`) never loaded → the control collapsed to height 0.
- Phantom tokens inherited from the Fable draft that don't exist in the theme:
`--color-neutral-content` (→ `--color-neutral-text`), `--state-hover`
(→ a `filter: brightness` hover); the pointer was transparent because its
fallback token didn't exist.
Design (per the user's reference image + directives):
- Value readout centred in the dial's hole (grid place-items).
- Donut arc: replaced an improvised hard-edged radial mask (aliased inner edge)
with an opaque face plate over the conic pie — a border-radius circle is
GPU-antialiased, so the inner ring is clean. Colours use theme-adaptive
tokens (surface-raised face, neutral-track ring, neutral-text value,
neutral-text-strong pointer).
- Value + label fonts scale with the knob via the same size coordinate as the
diameter (`--size-{k}-*` bundle), not an invented "one step below" rule.
Demo: value composed inside the Control (centre), plus a `needle` control that
conditionally composes `<Knob.Indicator>` (the compositional way to show/hide it).
Verified structurally (sizes, tokens, toggle) via DOM inspection; the screenshot
tool timed out all session, so the final pixel check is the user's. Recipe
contract green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two corrections after verifying the relocated Knob against the recipe contract:
1. The Knob was NEVER a contract violation — every `--knob-*` alias in the css
is consumed WITH a fallback (`var(--knob-x, default)`), which the
recipe-css-contract treats as runtime-optional, so no recipe-token
declaration is required. Removing `knob` from `WIP_TRACKS` keeps the full
eidos suite green (289) — it's now first-class, not an excluded WIP track.
2. But `size` was a visual no-op: the css hardcoded `--size-md-*` everywhere,
so `data-size` did nothing. Introduce private `--_knob-*` size vars selected
by `[data-size]` off the global `--size-{k}-*` bundle (public `--knob-*`
still overrides; the size-driven private is only the default; value readout
sits one step below). Browser-verified: control diameter now sm 60 / md 72 /
lg 88 px (was fixed 72 at any size).
npm run check unchanged at 59 (baseline); no console errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Canonical-depth demo at web/routes/uix/components/knob mirroring the Slider
demo (Live / API / morfo / sema / recipe / a11y tabs; every public prop is a
live control; sema event trace + play buttons; soma/eidos code snippets).
Serves as the visual/behavioral verification the relocation lacked. Confirmed
live at /uix/components/knob:
- renders role=slider, aria-valuemin/max/now, aria-roledescription resolved
from the langs catalog ("dial giratorio" under the es locale);
- provider-published `--knob-angle` / `--knob-progress` drive the conic-gradient
arc + rotated indicator (angle = startAngle + progress*sweep, verified exact);
- keyboard loop works end-to-end (ArrowUp/PageUp → value 40→52, aria-valuenow +
CSS vars + bound ValueText all update); no console errors.
npm run check: 0 errors in the demo (total unchanged at the 59 baseline).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ships the net that batches A1-A3 / B3 land against, mirroring
scripts/docs-check.ts (textual invariants, no Svelte-graph imports):
- A-readme (error): every src/arts/{name}/ has a README.md. ethereal is
exempt via a self-cleaning README_PENDING set until batch A3 authors it.
- A-naming (error): the Engine*/Active* split holds — no Engine* root is
declared in a .svelte.ts (engines are stateless), and every reactive
`class Active*` root lives in a .svelte.ts (Active...Error exempt).
- A-index (warn): the src/arts/README.md Map lists every art. WARN, not
error — the four current gaps (bus, ethereal, orca, prefs) are batch B3's
worklist and drive to zero there.
Green now: 0 errors, 4 warnings (the B3 worklist), 22 arts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
User decided (mirroring F7's corpus-shape call): arts stay
E5-in-place-reconciled with only active-app promoted to an architecture
chapter; orca stays E5 for now; the DESIGN docs move to docs/decisions/
translated. B1/B2 locked; A1/A2 (drift + language) unaffected.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>