11 KiB
ETAF Module Responsibilities and Target Architecture (Unimplemented Proposal)
Status: design proposal. This is not the delivered API. Current behavior remains defined by
architecture.en.md,user-guide.en.md, and passing tests.
This document defines only the target model, module owners, dependency direction, and completion criteria. Delivery sequencing belongs in a separate implementation plan; historical names and compatibility do not shape the target architecture.
1. Final model
Component semantics and ownership
↓
View: Text / Box / Fragment
↓
Ebox: measurement, layout, geometry patches
↓
TP + Emacs adapter: paint and final submission
The main public concepts are Text, Box, and Component. Fragment is advanced
structure, Host is an internal Renderer term, and an Ebox Node is a backend object.
The complete normalized View shape is:
View = Text
| Box
| Fragment
| ComponentCall
expr and slot are computation/projection mechanisms, not visual nodes. The target
public View does not accept raw Ebox Nodes. Missing rendering capability must become a
typed View/Ebox feature with explicit identity and impact contracts rather than an
opaque escape.
2. Text, Box, and Fragment
2.1 Text
Text is a leaf that owns strings and inline Text runs, typography/paint, text measurement and wrapping input, and optional identity/semantic/event properties.
Text defaults to :outer 'inline. It cannot own Box/Component children or establish
row, column, flex, or grid layout. Use an outer Box for padding, border, dimensions, or
child layout.
2.2 Box axes
:outer = inline | block
:layout = normal | row | column | flex | grid
:outer controls participation in a parent normal layout. :layout controls child
layout. Defaults are Box: block/normal and Text: inline.
(box :outer 'inline :layout 'flex ...)
This is inline-flex: an orthogonal combination, not another node type. ETAF exposes
:outer and :layout, not :inner or Ebox's raw display-pair representation.
2.3 Bounded normal layout
Normal layout supports only:
- consecutive inline Text/Box nodes in one wrapping line flow;
- block Boxes starting a new independent block;
- stable child order and block boundaries ending adjacent inline lines.
It does not implement full browser CSS: no floats, tables, absolute/fixed positioning,
run-in, list-item, or arbitrary anonymous-box rules. Ebox may map normal to an
internal flow algorithm; flow is not ETAF vocabulary.
For row, column, flex, or grid parents, the parent algorithm owns item placement and a
child's :outer does not change ordering. :outer is consumed only by a normal parent.
2.4 Other layout modes
| Mode | Responsibility |
|---|---|
row |
simple horizontal order without Flex distribution |
column |
simple vertical order without Flex distribution |
flex |
Flex sizing, direction, wrapping, alignment, and gaps |
grid |
two-dimensional tracks, placement, spans, and gaps |
They are Box modes, not Components or alternate Runtime node names. The only canonical
surface is (box :layout 'MODE ...); core provides no layout-name aliases.
2.5 Fragment
Fragment is a nonvisual structural Range. It may contain zero, one, or many sibling
Views, creates no Box or layout context, and may carry only a stable :key plus
children. Runtime may retain and replace its Range independently.
A Component call has no fixed outer/layout properties. Its root Text/Box determines participation. A transparent Component returning a Fragment may have multiple roots.
2.6 Strings, content, and empty Boxes
Strings under a Box normalize to Text, so (box "text") equals
(box (text "text")). ETAF does not expose (box :content ...); :content remains an
Ebox backend field. ETAF also has no spacer type: a childless Box is an empty Box.
3. Property contract
Properties form closed, owner-specific schemas. Unknown or inapplicable combinations fail explicitly.
| Owner | Category | Representative properties |
|---|---|---|
| Text | outer participation | :outer |
| Text | text/paint | :face, :color, :bgcolor, :wrap-mode, :text-align |
| Box | outer/inner layout | :outer, :layout |
| Box | geometry/surface | width/height/min/max, margin, padding, border, box sizing, overflow |
| Flex container/item | layout/participation | direction, wrap, alignment, gap, grow/shrink/basis/order |
| Grid container/item | layout/participation | tracks, auto flow, placement, spans, alignment, gap |
| Text/Box | identity/semantics | :key, :ref, class/id/role/ARIA/events/Behaviors |
Key rules:
:keyis identity metadata, not paint;- outer/layout/layout-specific properties have structure/geometry impact;
- color/background/typography have paint impact;
- ECSS preserves that impact classification;
- TP consumes resolved paint contributions only;
- structural changes transactionally replace the required layout context.
4. Module owners
ETAF Core
etaf-view owns normalized View shapes, string-to-Text normalization, closed property
schemas, slots, keys, and expression boundaries. It does not measure, lay out, or write
buffers.
etaf-component owns Component props and :view/:setup/:styles definitions. It
does not schedule Runtime or call Ebox.
etaf-runtime owns Component/Fragment/Range identity, reactive dependencies, Context,
Theme, Actions, Behaviors, Data, Resources, lifecycle, candidate generations, commit
authority, rollback, and disposal. It does not compute pixels.
etaf-renderer is the only ETAF-to-Ebox lowering adapter. It consumes normalized View
and computed properties without accessing Ebox private state.
Theme token meaning and inheritance belong to ETAF Context, not TP.
Ebox
Ebox owns Text measurement/wrapping/line layout, normal/row/column/flex/grid geometry, the box model, overflow/scroll/viewport behavior, stable Ebox node identity, geometry snapshots, and structural patch plans. It does not know Component semantics or paint contribution priority.
Logical boundaries:
ebox-core pure measurement, layout, snapshots, structural patches
ebox-emacs font/window capabilities, buffer positions, structural submission
These need not become separate distribution packages immediately, but must remain independently testable.
ECSS
ECSS owns selectors, cascade, inheritance, and computed properties. Its output retains structure/geometry/paint impact. ECSS does not run layout or write buffers.
TP and commit boundary
TP owns paint-contribution priority, paint-operation merging, Emacs text-property journaling, application, and rollback.
ETAF Runtime creates a candidate and owns commit authority
↓
Ebox stages structural/geometry patches
↓
TP stages paint patches
↓
Emacs adapter atomically applies both
↓
ETAF Runtime promotes the generation; failure rolls participants back
Ebox does not own TP priority, TP does not own layout, and neither participant may promote a Runtime generation.
Upper packages
| Package | Owns | Does not own |
|---|---|---|
etaf-ui |
ordinary reusable Components | another Runtime or layout engine |
etaf-sqlite |
SQLite Data Source and its transactions/disposal | Data Controller or UI |
etaf-performance |
application-neutral operations/stages/statistics/reports | example semantics or scheduling authority |
ebox-playground |
public Ebox examples/verification | ETAF |
etaf-playground |
ETAF/UI/SQLite composition examples and tooling | core protocols |
etaf-performance is a separate optional package. No measured package depends on it;
it observes only public boundaries. A Playground .etaf manifest belongs to the
Playground. Any future general compiler must lower to the same View/Component contract.
5. Dependency direction
etaf-ui ─────────────▶ ETAF Core ─────────────▶ Ebox public port
etaf-sqlite ─────────▶ ETAF Data contract
etaf-playground ─────▶ ETAF + optional UI/SQLite
ebox-playground ─────▶ Ebox only
etaf-performance ────▶ public observation boundaries only
ETAF Renderer ───────▶ ECSS computed properties
ETAF Runtime ───────▶ Ebox/TP transaction participants
Reverse dependencies are forbidden: Ebox never depends on ETAF, TP never parses View, ECSS never writes buffers, UI never calls Ebox private APIs, SQLite never knows UI, and Playgrounds never inject protocols into core.
6. Rust and Elisp
Boundaries precede implementation language. Deterministic kernels may move to Rust only after contracts and equivalence tests are stable:
Rust candidates:
normalized View validation and structural diff over opaque stable IDs
ECSS cascade
Text-measurement input processing and Ebox layout
pure geometry/paint patch computation
Elisp / Emacs:
user Components, refs, Context, Actions, Data, Resources, lifecycle
database and external I/O
Emacs events, font/window capabilities, and final submission
Component identity, dependency ownership, TP priority, and generation authority do not move merely because a kernel is written in Rust. Elisp must not repeat Rust diff, cascade, layout, or text scans.
7. Correctness and performance invariants
- View, computed properties, geometry snapshots, and paint contributions materialize once per transaction;
- Text paint changes do not execute unrelated Components or layout;
- outer/layout/geometry changes affect only the required layout owner;
- Fragment/slot/list changes replace only their Range;
- Ebox/TP/Emacs participant failure preserves the last committed generation;
- resize uses current window facts immediately, without hidden debouncing semantics;
- instrumentation does not change the measured path.
Final gates cover structural equivalence, legal/illegal outer-layout combinations, normal inline/block behavior, row/column/flex/grid GUI behavior, Component roots and Fragments, style/Theme/TP priority and rollback, continuous Research Shelf/Flex reference resize, and p95/max at or below 50ms in fixed real scenarios.
8. Current gap and non-goals
Current code still registers text, fragment, container, row, column, stack,
flex, grid, and spacer, and exposes raw-ebox. Target box, :outer, and
:layout 'normal are not implemented, and the target public View does not retain
raw-ebox.
This is a clean redesign: old Host names and old .etaf sources need not continue to
run, and no long-lived compatibility layer is added. Formal architecture/user docs must
not present target syntax as delivered before implementation is complete.
Non-goals:
- full browser CSS;
- spacer, public flow, or
(box :content ...); - injecting raw Ebox Nodes into View;
- layout modes as Components or alternate Runtime nodes;
- old Host aliases or optional layout shorthand in core;
- core protocols owned by Playground/UI/database packages;
- App precompilation without measured compile cost and runtime benefit.