Rule
Rule specification
What Rule Is
Section titled “What Rule Is”Rule is Proto UI’s declarative syntax for “when these conditions hold, produce this clear intent.” It is not an information channel, and it does not directly carry information between Component, User, App Maker, or other Components.
Rule is useful because it preserves prototype-author intent as an analyzable structure. Imperative callbacks can express many of the same behaviors, but adapters cannot reliably understand what that code is trying to do. Rule separates condition from intent so adapters can discover optimization paths without compiler-grade static analysis.
Rule is optional syntax. Removing Rule should not make prototypes unable to express the same class of behavior. But when the logic naturally fits “condition holds, then produce intent,” Rule is the recommended path.
Execution Periods
Section titled “Execution Periods”Rules are declared during setup:
def.rule(({ when, intent }) => ({ when: when.eq(when.props('disabled'), true), intent: intent.feedback.style.use(tw('opacity-50')),}));def.rule(spec) only records when and intent. It does not read current runtime props during setup, write state, write feedback, or touch the host.
Removal or undo functions returned by setup-only APIs are setup-only by default. If Rule declaration removal exists, it cannot be retained in a closure and used during runtime as a lifecycle disposer or escape hatch.
runtimeDuring runtime, Rule evaluates RuleIR against current observable inputs, selects active Rules, collects intent in declaration order, and produces a semantic Plan. The stable v0 default Plan is style.tokens.
The default runtime path does not directly write DOM classes, attributes, native views, or other host objects. Adapters or extension modules may short-circuit the Plan and generate optimized host artifacts, but those artifacts do not define Rule core semantics.
when is a pure condition expression. It reads observable inputs provided by other modules and produces a boolean.
The v0 core expression set includes:
truefalseeq(a, b)not(expr)all([...exprs])any([...exprs])eq uses strict equality. It does not coerce types, perform deep comparison, or use custom comparators. all([]) is true; any([]) is false.
Rule does not directly match instantaneous event occurrences. Event-driven Rule logic must first express the event dimension as observable state, such as hover, pressed, or focused interaction state, and then let Rule read that state.
when Input Sources
Section titled “when Input Sources”Rule core does not create input sources. Props, state, context, or other inputs used by when must be provided by their modules or by Rule extensions.
Stable core coverage currently includes:
| Input source | Current semantics |
|---|---|
props | Reads resolved props, depends on prop identity, and may re-evaluate when props change |
state | Reads current state view values; Owned / Borrowed / Observed views can all be observed |
context | Reads the whole context value; resolves to null when no provider is present |
Context path access remains implementation debt. The core contract currently stabilizes only whole-value context dependencies, not w.ctx(key).path(...).
Host/environment inputs are also outside stable core naming for now. Existing meta practice is secondary or still governed, and may later be replaced by a more systematic environment/configuration model.
intent
Section titled “intent”intent records analyzable operations rather than executing operations during setup. It cannot carry arbitrary callbacks or host imperative logic inside RuleIR.
Each intent channel defines its own merge and execution semantics. Rule core does not provide implicit cross-channel priority.
feedback.style Intent
Section titled “feedback.style Intent”The stable v0 Rule intent is feedback.style:
def.rule(({ when, intent }) => ({ when: when.eq(when.state(pressed), true), intent: intent.feedback.style.use(tw('translate-y-px opacity-80')),}));i.feedback.style.use(...handles) records visual feedback style intent. It does not widen the feedback.style token language; inputs still follow feedback.style handle and token constraints.
Style tokens from active Rules are collected in Rule declaration order and passed through feedback semantic merge. When a Rule becomes inactive, its style token contribution must be removed on the next evaluation.
The flow is:
observable inputs -> when evaluation -> active rule intents -> style.tokens plan -> feedback semantic merge -> feedback runtime style layer -> translation layerRule produces translatable style semantics, not DOM classes or CSS selectors. Web selector optimization is a downstream result of Rule’s design idea, not a Rule core constraint.
Deferred Scope
Section titled “Deferred Scope”The following areas are recorded as decisions or debt, but are not stable v0 Rule core:
| Area | Status |
|---|---|
intent.state | Planned capability; layer stack, rollback, baseline, and reason semantics are not v0 guarantees yet |
| context path access | Only whole-value context dependency is stable today |
meta naming | Host/environment input abstraction and naming remain under governance |
| RuleIR live handle side tables | Implementation must continue moving live handles out of RuleIR |
| setup-time removal handles | setup-only removal must not become a runtime disposer |
These areas may appear in fixtures or planned tests, but they are not current required matrix coverage.
Contract Previews
Section titled “Contract Previews”Test Mapping
Section titled “Test Mapping”Rule tests are organized as dimension tests plus matrix tests, so coverage does not explode combinatorially:
| Test entity | Main coverage |
|---|---|
T-RULE-0001 | def.rule setup-only, RuleSpec recording, RuleIR serialization, setup-only removal |
T-RULE-WHEN-0001 | pure when expressions, strict equality, logical expressions, event boundary |
T-RULE-WHEN-0002 | props/state/context dependency dimensions |
T-RULE-INTENT-0001 | intent operation recording and channel independence |
T-RULE-INTENT-FEEDBACK-STYLE-0001 | stable feedback.style intent |
T-RULE-MATRIX-0001 | props/state/context × feedback.style stable matrix |
intent.state combinations may appear in the matrix fixture, but they are deferred cells rather than current required coverage.
Related Specs
Section titled “Related Specs”Coredefines setup/runtime phase boundaries; Rule declarations are setup-only, and Rule evaluation happens at runtime.Props,State, andContextcan provide observable inputs forwhen, but Rule does not own those information sources.Eventis not directly matched by Rule; event dimensions should first become observable state.Feedbackdefinesfeedback.styletokens and semantic merge; Rule’s stable v0 intent reuses those semantics.Lifecyclegoverns instance lifetime and runtime callback boundaries; Rule setup-time removal cannot become a lifecycle disposer.Adapterand Rule extensions may optimize execution, but must preserve semantic equivalence with the default Plan.