Skip to content

Freehand Conformance Index

Type: Reference One row per Freehand executable law. The id scheme, the allocation rule, the column contracts, the applicability grammar, and the status vocabulary are defined in README.md — read it before adding a row.

The index is the single roster of Freehand laws. It is empty at establishment and grows one row at a time: each slice that lands a contract appends its own rows to its own area section, in the same change as the spec paragraph each row cites. Nothing here is normative — every row is an address into spec/, plus the fixture that proves the paragraph it names.

Row shape, for reference — a template, not an allocation:

| `FH-AREA-NNN` | one line stating what is proven | [00X-Doc.md#anchor](../../00X-Doc.md#anchor) | common jvm browser | `spec/conformance/freehand/fixtures/fh-area-nnn.edn` | active |

Areas

FH-CALL — Calls

The declared boundary: descriptors, plain helpers, children, :key, occurrence identity, hot reload, rejected declaration forms.

Id Law Canonical paragraph Applicability Fixture Status
FH-CALL-001 A declared view cannot be successfully called: it is not a function and not a map, and a direct call raises :rf.error/view-called-directly naming the three legal recoveries, on both hosts and at every arity the host's call protocol declares 004-Views.md#a-declared-view-cannot-be-called common jvm browser spec/conformance/freehand/fixtures/fh-call-001.edn active
FH-CALL-002 Vector-head classification is total: a descriptor is an internal boundary, a keyword is a DOM/custom element, a declared host descriptor is a foreign boundary, and anything else raises naming those three legal forms 004-Views.md#vector-head-classification common jvm browser spec/conformance/freehand/fixtures/fh-call-002.edn active
FH-CALL-003 The inspection projection carries exactly the public descriptor ABI; the render body and the host mount/tree entries stay private, and an undeclared props schema is reported as absent rather than :any 004-Views.md#the-inspection-projection common jvm browser spec/conformance/freehand/fixtures/fh-call-003.edn active
FH-CALL-004 Interpreted and compiled descriptors mount as children in either direction through the ordinary named-descriptor boundary; all four pairings of parent mode and child mode over one body yield one structural tree 004-Views.md#cross-mode-children common jvm browser spec/conformance/freehand/fixtures/fh-call-004.edn active
FH-CALL-005 v/markup mounts markup held as a value through an ordinary declared interpreted child: the value renders, and the child owns its own boundary node, recorded props and expansion rather than being inlined into the compiled parent 004-Views.md#the-vmarkup-boundary common jvm browser spec/conformance/freehand/fixtures/fh-call-005.edn active
FH-CALL-006 A v/slot renders the v/render-fn content its caller supplied, with the arguments the component supplies, identically in both execution modes: the output participates in the surrounding children like any other child with no slot node or wrapper, an absent slot renders nothing while its siblings close over the gap, and a slot-carrying prop records on the boundary as the :v/render-fn opaque marker 004-Views.md#render-slots common jvm spec/conformance/freehand/fixtures/fh-call-006.edn active

FH-PROPS — Props

One props map: reserved :children, stripped :key, equality and conversion, optional schema semantics.

Id Law Canonical paragraph Applicability Fixture Status
FH-PROPS-001 An internal boundary call carries exactly one props map and no positional arguments; a missing or non-map props slot is rejected 004-Views.md#props-children-and-key common jvm browser spec/conformance/freehand/fixtures/fh-props-001.edn active
FH-PROPS-002 Trailing children arrive as the reserved :children vector — absent when there are none; a caller-authored :children is rejected and the declared children policy is enforced at the call 004-Views.md#props-children-and-key common jvm browser spec/conformance/freehand/fixtures/fh-props-002.edn active
FH-PROPS-003 :key selects sibling identity, is stripped before the props map reaches the view, and is outside props equality 004-Views.md#props-children-and-key common jvm browser spec/conformance/freehand/fixtures/fh-props-003.edn active
FH-PROPS-004 A props schema is OPTIONAL — a declaration without one compiles, renders, and reports its schema as absent rather than as :any — and a declared one closes the props map to the keys it names, returning the same accept/reject verdict over the same props in both execution modes, with :key and :children never schema slots — a literal schema naming either is refused at the declaration — and {:closed false} the one explicit escape; the modes differ only in when a breach is reported, the compiled tier at build time and the boundary at render 004D-Freehand-Compiled-Grammar.md#props-schemas common jvm browser spec/conformance/freehand/fixtures/fh-props-004.edn active
FH-PROPS-005 Every view the public door publishes declares a props schema, and the gate that says so names any published view that omits one — the mandate falling on published surfaces and generated-parity claims rather than on the grammar, and decided by the publishing surface rather than by a declaration option 004D-Freehand-Compiled-Grammar.md#where-a-schema-is-mandatory common jvm spec/conformance/freehand/fixtures/fh-props-005.edn active
FH-PROPS-006 (v/spread base overrides) folds both runtime attribute maps onto the element later-arg-wins, judged key by key by the rule a literal attribute map is judged by; (v/spread-safe owned caller) denies the caller the structural/controlled keys and the component's own handler families in every build — in every spelling that reaches the same emitted slot — and folds what survives UNDER the owned props with :class composing owned-first; both answer the same tree in both execution modes 004-Views.md#props-forwarding common jvm spec/conformance/freehand/fixtures/fh-props-006.edn active

FH-EVENT — Events

Projection materializer, the closed listener-options grammar, site and proxy lifetime, the atomic selected bundle.

Id Law Canonical paragraph Applicability Fixture Status
FH-EVENT-001 The event site materializes the closed ::v/value / ::v/checked / ::v/key / ::v/scroll-top / ::v/new-state roster from the live callback payload into top-level argument positions only — every occurrence, never a nested one, position zero never a marker, an unavailable payload a typed error with no dispatch — and general re-frame dispatch gains no payload arity 004-Views.md#event-intent-and-the-payload-materializer common jvm browser spec/conformance/freehand/fixtures/fh-event-001.edn active
FH-EVENT-002 An event-producing site yields exactly one event vector or nil after the closed listener options are interpreted; a map at an event position is the listener-options map and nothing else, so nil dispatches nothing, a multi-intent vector and an unknown option key — a string key included — are rejected in both tiers, and :once is site state retained across re-render 004-Views.md#event-intent-and-the-payload-materializer common jvm browser spec/conformance/freehand/fixtures/fh-event-002.edn active
FH-EVENT-003 The callback roster is closed and classification over an event position is total: each legal form resolves to exactly one role, anything else is rejected naming the roster, and v/render-fn / v/raw-fn sit outside the committed-proxy scheme 004-Views.md#callback-roles-and-identity common jvm browser spec/conformance/freehand/fixtures/fh-event-003.edn active
FH-EVENT-004 Each event site owns one stable committed proxy bound to the incarnation that minted it: identity survives re-render, equal values at two sites stay independent, a never-selected candidate's proxy is never a doorway, a later commit retargets the body without changing the identity, and a retired proxy stays inert even when its key is re-used 004-Views.md#callback-roles-and-identity common jvm browser spec/conformance/freehand/fixtures/fh-event-004.edn active
FH-EVENT-005 Withdrawn. The closed exact-key key-condition map on :on-key-down/:on-key-up selected one intent by KeyboardEvent.key; its delete-before-release gate (D007) was discharged with outcome DELETE on 2026-07-25, so the form is gone from the substrate and the law no longer binds. A map at an event position is the listener-options map and nothing else — FH-EVENT-002 — and keyboard branching is an ordinary registered event carrying ::v/key. The id is burned rather than freed, so an old citation still resolves to that answer and no later EVENT law inherits it 004-Views.md#event-intent-and-the-payload-materializer common jvm browser retired

FH-INPUT — Controlled input

The exact door predicate, the frame-scoped synchronous flush, the browser contention matrix.

Id Law Canonical paragraph Applicability Fixture Status
FH-INPUT-001 The controlled-input door is one exact predicate over five final-normalized facts — a supported native control tag, value/checked present by PRESENCE, a firing attribute normalizing to onInput/onChange, a synchronously-known event-vector-or-nil outcome, and no :capture/:passive lane — read as normalized SLOTS so every spelling of one emitted prop is judged alike; :on-before-input is outside, each near-miss is one named fact from inside, and both execution modes ask this predicate rather than a copy of it 004-Views.md#controlled-inputs common jvm browser spec/conformance/freehand/fixtures/fh-input-001.edn active
FH-INPUT-002 A site inside the door fires through a SYNCHRONOUS dispatcher bound to the exact frame its commit published — the event drains and the cells observing that frame are flushed before the call returns — while a site outside it takes the ordinary batched dispatcher into the same frame; the flush is FRAME-scoped, so a pending cell observing another frame is left pending with no revision advanced, a nil outcome reaches neither lane, a boundary under no frame has no door, and the verdict rides the committed plan so a re-commit moves it without changing one callback identity 004-Views.md#controlled-inputs common jvm browser spec/conformance/freehand/fixtures/fh-input-002.edn active
FH-INPUT-003 In a real browser the controlled round trip holds as a user-visible fact: sustained and rapid typing produces a final value equal to the typed string character for character, the caret and a range selection survive the round trip unmoved, an IME composition completes intact across compositionstart/compositionupdate/compositionend, none of it degrades while a heavy sibling on the same frame is already dirty, and state going explicitly nil clears the live node rather than leaving the last rendered value on screen 004-Views.md#controlled-inputs interpreted browser spec/conformance/freehand/fixtures/fh-input-003.edn active

FH-SUB — Subscriptions

Render-only reads: value, resolution, invalidation, commit safety; one-shot reads; frame-context observation and compiled-elision proof.

Id Law Canonical paragraph Applicability Fixture Status
FH-SUB-001 The selected commit publishes the boundary's frame, dependencies, event site table and evidence as one bundle; a render itself owns nothing, an unchanged site is retained untouched, and every newly-observed or retargeted target is acquired before anything is released 006-ReactiveSubstrate.md#the-selected-render-bundle common jvm browser spec/conformance/freehand/fixtures/fh-sub-001.edn active
FH-SUB-002 A candidate the host never selects publishes nothing at all, and a candidate whose reconcile fails releases everything its staging acquired and leaves the prior committed set installed 006-ReactiveSubstrate.md#abandoned-and-failed-candidates common jvm browser spec/conformance/freehand/fixtures/fh-sub-002.edn active
FH-SUB-003 A candidate rendered against a body revision the cell has since replaced is stale and publishes nothing — checked at commit entry and again at the narrowest publication boundary — and a redeclared view remounts its boundary, releasing exactly what the old body owned 006-ReactiveSubstrate.md#body-authority-across-a-live-cell common jvm browser spec/conformance/freehand/fixtures/fh-sub-003.edn active
FH-SUB-004 The interpreted shell observes frame context unconditionally, so a provider retarget rebinds a props-equal child: dependencies are acquired against the new frame before the old are released, the committed event destination moves with them, no callback identity changes, and a render whose exact frame incarnation is gone publishes nothing 006-ReactiveSubstrate.md#frame-binding-and-retarget interpreted jvm browser spec/conformance/freehand/fixtures/fh-sub-004.edn active
FH-SUB-005 Each occurrence owns its own bundle and a commit publishes into that occurrence only; a keyed reorder moves occurrences rather than rebuilding them, and a reorder inside one boundary disposes neither node 006-ReactiveSubstrate.md#occurrence-identity-across-a-reorder common jvm browser spec/conformance/freehand/fixtures/fh-sub-005.edn active
FH-SUB-006 Disconnect releases every dependency, retires every published callback and drops the cell from the pending window; it is not terminal, and a host's replay of the same committed render reconnects from a clean slate rather than resurrecting released ownership 006-ReactiveSubstrate.md#disconnect-and-replay common jvm browser spec/conformance/freehand/fixtures/fh-sub-006.edn active
FH-SUB-007 Capture is same-render-thread only: a read with no active render is refused, and a read reached through a conveyed child thread fails before it probes, so a fork records nothing 006-ReactiveSubstrate.md#same-render-thread-capture common jvm browser spec/conformance/freehand/fixtures/fh-sub-007.edn active
FH-SUB-008 v/sub during render returns the subscription's current value and records a render-owned read; the selected commit publishes those reads as the bundle's dependency set — exactly the queries read, in render order, each an owned dependency 006-ReactiveSubstrate.md#a-render-owned-value common jvm browser spec/conformance/freehand/fixtures/fh-sub-008.edn active
FH-SUB-009 v/sub is legal only during an active declared render; a read with no render to own it is refused loudly with a stable diagnostic id, raised before the target is resolved, rather than probed and dropped to a silent nil 006-ReactiveSubstrate.md#the-render-only-rule common jvm browser spec/conformance/freehand/fixtures/fh-sub-009.edn active
FH-SUB-010 The capture rides the active render, not the call depth: a v/sub inside an ordinary defn helper called from the body is owned by the calling render exactly as an inline read is, and the same helper called outside a render is refused loudly 006-ReactiveSubstrate.md#capture-through-helper-functions common jvm browser spec/conformance/freehand/fixtures/fh-sub-010.edn active
FH-SUB-011 When an input a committed v/sub depends on changes value, a re-render recomputes it and the commit republishes the whole bundle atomically: the committed bundle is unchanged until the next commit, and the recommitted dependency then carries the new value against the same retained handle, flipping as a unit 006-ReactiveSubstrate.md#invalidation-and-atomic-recommit common jvm browser spec/conformance/freehand/fixtures/fh-sub-011.edn active

FH-CTRL — Controllers

Props-only default, frame data keyed by kind plus explicit address, semantic transitions, owner cleanup, and the buffered controller's required reset generation.

Id Law Canonical paragraph Applicability Fixture Status
FH-CTRL-001 A controller record is keyed by the controller kind plus the caller-supplied :control address, so two occurrences of one view given DIFFERENT addresses hold independent records: an edit at one occurrence moves only its own record, and the other keeps showing its baseline 004-Views.md#controller-identity common jvm browser spec/conformance/freehand/fixtures/fh-ctrl-001.edn active
FH-CTRL-002 Two occurrences of one view given the SAME :control address share ONE record deliberately — an edit at either occurrence is read by both, no diagnostic fires, and the sharing is a consequence of the caller supplying the address rather than an accident of render position 004-Views.md#controller-identity common jvm browser spec/conformance/freehand/fixtures/fh-ctrl-002.edn active
FH-CTRL-003 A writable controller rendered with no :control address is refused loudly with :rf.error/view-control-address-missing naming the kind and the recovery — no default address, no synthesised one — and one rendered with an EXPLICIT nil address is refused just as loudly under a DIFFERENT id, :rf.error/view-control-address-nil, because a present-but-nil address is a different mistake with a fix upstream of the call site; a falsey-but-present address is ordinary and renders, as does the same view given a real address, and a props-only view that never asks for a record key is untouched 004-Views.md#controller-identity common jvm browser spec/conformance/freehand/fixtures/fh-ctrl-003.edn active
FH-CTRL-004 The causal owner's ordinary event clears the addresses it owns and the records are gone, while a record whose owner has not cleared it survives; the substrate exposes NO lifecycle cleanup hook — no unmount callback, dispose registration or per-occurrence teardown slot on the public surface or in the declared-view ABI — so retention follows the owner, never the render 004-Views.md#semantic-transitions-and-owner-cleanup common jvm browser spec/conformance/freehand/fixtures/fh-ctrl-004.edn active
FH-CTRL-005 Controller state is ordinary frame data: the record a controller wrote is read back through the normal re-frame path — a plain subscription over app-db, with no view mounted and no controller-specific reader — and a semantic transition is an ordinary registered event 004-Views.md#controller-state-is-ordinary-frame-data common jvm browser spec/conformance/freehand/fixtures/fh-ctrl-005.edn active
FH-CTRL-006 A buffered controller rendered with no :reset-key is refused loudly with :rf.error/view-control-reset-revision-missing naming the kind and the recovery — the reset generation is required, never optional and never inferred from the value — and one rendered with an EXPLICIT nil revision is refused just as loudly under a DIFFERENT id, :rf.error/view-control-reset-revision-nil, because nil is not a generation and the fence would read every draft under it as superseded; a falsey-but-present revision is ordinary and renders, as does the same control given a real revision, a stable literal is a legitimate revision, and a controller that holds no draft is untouched 004-Views.md#the-buffered-controller-and-the-reset-generation common jvm browser spec/conformance/freehand/fixtures/fh-ctrl-006.edn active
FH-CTRL-007 A caller rejects a live draft by advancing the reset generation, and the rejection lands even when the value it reasserts is EQUAL to the one it had: the draft stops being displayed at once and the caller's baseline is shown, while the same reassertion under an unchanged generation leaves the draft alone — the case value-equality is provably blind to 004-Views.md#the-buffered-controller-and-the-reset-generation common jvm browser spec/conformance/freehand/fixtures/fh-ctrl-007.edn active
FH-CTRL-008 Work from a superseded generation lands in neither direction: a commit whose generation no longer matches the committed record produces no caller intent, an edit made under a generation the caller has replaced is born stale and is neither displayed nor committable, a cancel clears the record before a later blur can resurrect it, and a repeated commit is an idempotent no-op — each decided in the handler against committed state, never by a guard captured during render 004-Views.md#the-generation-fence common jvm browser spec/conformance/freehand/fixtures/fh-ctrl-008.edn active
FH-CTRL-009 The reset target is the caller's CURRENT baseline, re-read by the render that follows the reset rather than restored from a mount-time snapshot; and reaching it costs no render-time mutation — repeating the render, including under candidates the host never selects and across a hot-reload body-generation advance, dispatches nothing, moves no controller state and yields an identical tree 004-Views.md#the-generation-fence common jvm browser spec/conformance/freehand/fixtures/fh-ctrl-009.edn active
FH-CTRL-010 Many buffered controllers in one view carry independent generations and reset independently in ONE render pass, deterministically and in any combination; the discard is attributable from ordinary frame data alone — one caller event moves one location, every superseded record keeps the stamp that explains why it was orphaned — and no published door var is a controller reset channel 004-Views.md#the-generation-fence common jvm browser spec/conformance/freehand/fixtures/fh-ctrl-010.edn active
FH-CTRL-011 In a real browser a caller's rejection restores the baseline through the controlled-input path rather than by remounting: the input node is the same node before and after, focus and the caret survive the cycle, editing resumes at the new generation with the caret where the user put it, and an IME composition through the buffered draft completes intact on the node it started on 004-Views.md#the-buffered-controller-and-the-reset-generation interpreted browser spec/conformance/freehand/fixtures/fh-ctrl-011.edn active

FH-PRESENCE — Presence

Keyed retention, override, timeout, accessibility, and test contract.

Id Law Canonical paragraph Applicability Fixture Status
FH-PRESENCE-001 A presence boundary projects the :rf.ui/presence {:phase :present :timeout-ms n} fragment with its retained children :present, identically from an interpreted (v/presence …) and its {:compiled true} twin and on both hosts; the marker survives view-boundary adoption when presence is a view's root 004-Views.md#presence common jvm browser spec/conformance/freehand/fixtures/fh-presence-001.edn active
FH-PRESENCE-002 The keyed retention machine both modes lower to derives the three-phase plan — new keys mount, present keys hold, a departed key is retained :unmounting, a re-entered key flips back to :present, and first-appearance order is frozen against an incoming reorder — and claims one pair per key, the first claimant winning and duplicates reported 004-Views.md#presence common jvm browser spec/conformance/freehand/fixtures/fh-presence-002.edn active
FH-PRESENCE-003 In a real browser a departed key's child is retained :unmounting and owns its exit accessibility (aria-hidden read from (v/presence-phase)); the deterministic timeout removes it terminally and exactly once, and a re-entry before the flush cancels the removal and restores :present 004-Views.md#presence interpreted browser spec/conformance/freehand/fixtures/fh-presence-003.edn active
FH-PRESENCE-004 A structural render carries no lifecycle, so (v/presence-phase) answers :present there and a presence-aware child RENDERS rather than throwing — on both hosts, from an interpreted declaration and its {:compiled true} twin, inside a boundary and outside one, with no exit class and no aria-hidden stamped 004-Views.md#presence common jvm browser spec/conformance/freehand/fixtures/fh-presence-004.edn active

FH-TOPLAYER — Top layer

The popover/modal desired-state pair, commit and generation law, structural metadata, browser matrix.

Id Law Canonical paragraph Applicability Fixture Status
FH-TOPLAYER-001 A qualified desired-state property leaves :attrs and projects as the reserved :rf.ui/top-layer fact on the semantic element — the same value from an interpreted declaration and its {:compiled true} twin, on both hosts — while a nil value expresses no desired state and records nothing 004-Views.md#the-structural-projection common jvm browser spec/conformance/freehand/fixtures/fh-toplayer-001.edn active
FH-TOPLAYER-002 The desired-state pair is closed and qualified: popover-open? is legal only on an element with a valid :popover mode, modal-open? only on <dialog>, both together never, and a non-boolean value never — each refused at render with :rf.error/ui-tree-malformed on both hosts 004-Views.md#the-closed-pair-and-where-each-half-is-legal common jvm browser spec/conformance/freehand/fixtures/fh-toplayer-002.edn active
FH-TOPLAYER-003 In a real browser the pair yields the platform's own behaviour: a modal dialog takes initial focus, inerts the background and returns focus on close; two non-nested :auto popovers are one-at-a-time while a nested pair opened in ONE commit stacks and closes together in one call; and a browser-initiated dismissal reaches the author's handler without the substrate writing any application state 004-Views.md#native-behaviour-is-the-whole-point interpreted browser spec/conformance/freehand/fixtures/fh-toplayer-003.edn active
FH-TOPLAYER-004 Host work happens only at a selected commit and only when the desired state differs from the node's live state: building elements and detaching perform zero operations, a detach RETIRES its generation — withdrawing that generation's own pending operation even while its node stays connected, and never a successor's claim on the same node — a disconnected node is skipped, the operation lands before the first frame after the commit, N re-commits of an unchanged desired state perform zero further operations, and a full mount/open/close/unmount cycle retains exactly zero listeners, observers, intervals and pending operations 004-Views.md#commit-order-and-the-declared-tracking-frequency interpreted browser spec/conformance/freehand/fixtures/fh-toplayer-004.edn active
FH-TOPLAYER-005 The two development advisories are SELECTED-COMMIT evidence, typed: constructing an element's props publishes exactly zero records — a render the host may abandon accuses nobody — while the callback ref that runs only for a committed render publishes a stable catalogued :rf.warning/view-top-layer-unreconciled naming the mechanism and the handlers that reconcile it, and a host call the browser refuses publishes :rf.warning/view-top-layer-refused naming the call, the element and the browser's own reason before the runtime continues; both carry a :recovery, ride the trace diagnostic channel, and are eliminated under goog.DEBUG=false 004-Views.md#browser-dismissal-and-what-the-substrate-never-does interpreted browser spec/conformance/freehand/fixtures/fh-toplayer-005.edn active

FH-BEHAVIOR — Behaviors

Id/config/timing/optional-command protocol, commit-only connection, explicit command target, private memory, JVM marker and fallback.

Id Law Canonical paragraph Applicability Fixture Status
FH-BEHAVIOR-001 A behavior registration is closed in both directions: :timing is :passive or :layout and nothing else, a definition key outside the roster is refused rather than ignored, a non-function lifecycle entry is refused, and a declaration carrying no lifecycle and no commands is refused because it could never run 004-Views.md#the-registration-and-the-closed-timing-set common jvm browser spec/conformance/freehand/fixtures/fh-behavior-001.edn active
FH-BEHAVIOR-002 A behavior use site is data with a closed option roster: :use must name a registered behavior, :config is refused if it carries a non-data value at any depth, and the boundary decorates exactly one element — a second child, a declared view, a fragment or text is refused, as are Freehand children under an opaque behavior 004-Views.md#the-use-site-is-data common jvm browser spec/conformance/freehand/fixtures/fh-behavior-002.edn active
FH-BEHAVIOR-003 On the structural host a behavior is an inert marker: the boundary node records the registered id, the semantic target and the public config with the decorated element as its child, no lifecycle runs at all, and a command is refused with the channel's own diagnostic rather than silently dropped 004-Views.md#the-structural-marker common jvm spec/conformance/freehand/fixtures/fh-behavior-003.edn active
FH-BEHAVIOR-004 A behavior connects only from a SELECTED commit — a candidate render the host abandons connects nothing — and :update runs only when the committed config moves by rf=; a :layout behavior's work is complete before the browser paints 004-Views.md#commit-only-connection-and-total-cleanup interpreted browser spec/conformance/freehand/fixtures/fh-behavior-004.edn active
FH-BEHAVIOR-005 Teardown releases a behavior totally: :disconnect runs exactly once per committed connection, and after the last unmount the connection table and the live target index are both empty — measured against a control mount of the same markup that attached no behavior 004-Views.md#commit-only-connection-and-total-cleanup interpreted browser spec/conformance/freehand/fixtures/fh-behavior-005.edn active
FH-BEHAVIOR-006 A host command reaches the one live connection claiming its explicit semantic target in the effect's own frame and nothing else — a live decoy under a different id, and a connection a sibling frame committed, are both untouched, while two frames claiming one target are two addresses rather than one ambiguity — and an absent target, an ambiguous target, an unregistered operation and a malformed command are each refused with a typed diagnostic, performing no host work and queueing nothing 004-Views.md#the-bounded-command-channel interpreted browser spec/conformance/freehand/fixtures/fh-behavior-006.edn active
FH-BEHAVIOR-007 More than one mechanism may need a ref on one element, and the refs COMPOSE rather than clobber: participants receive the node from the inside out — the element's own intrinsics, then each decorating boundary outward — and release in the exact reverse under whichever release protocol each answered with; a mounted element carrying both a behavior and a top-layer intrinsic proves both received the same node and both released, measured against a control mount carrying neither 004-Views.md#sharing-one-node interpreted browser spec/conformance/freehand/fixtures/fh-behavior-007.edn active
FH-BEHAVIOR-008 The test/tool plane reads two read-only projections over the live behavior table — the active connections and a bounded window of command traffic — published through the ONE public door as v/active-connections and v/command-log, so a tool reads the plane without requiring an implementation namespace; and neither exposes a node, a private memory or any route to a host instance, adds an application event, or calls a tool back 004-Views.md#the-tool-plane interpreted browser spec/conformance/freehand/fixtures/fh-behavior-008.edn active
FH-BEHAVIOR-009 A connection's private memory is ESTABLISHED by :connect and nothing else writes it: :update, a delivered command and :disconnect receive the memory and their return values are ignored, so a void-returning host mutator — the ordinary shape of map.setOptions(…) or chart.update(spec) — cannot erase the instance the teardown must release 004-Views.md#the-registration-and-the-closed-timing-set interpreted browser spec/conformance/freehand/fixtures/fh-behavior-009.edn active

FH-REACT — React bridges

Descriptor-only outward bridge, common frame and props semantics, the explicit mapper, SSR policy, qualified host boundaries.

Id Law Canonical paragraph Applicability Fixture Status
FH-REACT-001 The outward bridge exports a DECLARED view and nothing else — a plain function, a hiccup vector, a view id keyword and a rendered form are each refused naming a declared view or an explicit wrapper as the recovery — and its option roster is closed at the single :map-props adapter, whose PRESENCE selects the adapter arm and whose value must be a function: an unknown option is refused rather than ignored, and a present-but-falsey :map-props is refused rather than read as an omitted key 004-Views.md#only-a-declared-view-crosses common browser spec/conformance/freehand/fixtures/fh-react-001.edn active
FH-REACT-002 Props cross by ONE shallow own-property pass matching exact names with values untouched — no camelisation, no deep walk — or by the one explicit adapter that returns the props map; and the three names the bridge owns behave as declared: frame is consumed, children becomes the boundary's trailing children under the view's own declared children policy, ref is refused, and a props map carrying the reserved :frame is refused naming the view 004-Views.md#one-shallow-prop-rule-or-one-explicit-adapter common browser spec/conformance/freehand/fixtures/fh-react-002.edn active
FH-REACT-003 Exporting one view with one adapter answers the IDENTICAL component object every time, including across a redefinition of the view — which republishes the new body through the component React already mounted rather than minting a new type — while a different adapter over the same view, and the same adapter over a different view, are each a different component 004-Views.md#the-exported-component-is-stable common browser spec/conformance/freehand/fixtures/fh-react-003.edn active
FH-REACT-004 An own frame prop SCOPES an already-live frame and creates, refreshes and destroys nothing; own-property presence decides, so an explicit nil, a malformed target and a target naming no live frame each fail loud and self-attributed to the bridge rather than falling through to the ambient chain, while an OMITTED prop resolves the frame ambiently and fails with the ordinary no-frame-context diagnostic under no frame boundary at all 004-Views.md#a-frame-is-selected-never-created common browser spec/conformance/freehand/fixtures/fh-react-004.edn active
FH-REACT-005 The bridge is a browser verb and is ABSENT on the JVM — the door publishes it under the same reader conditional as the mount verbs — so no server render can contain an exported component, and the bridge reads no server-renderer context internal to make one work 011-SSR.md#the-outward-react-bridge-has-no-server-arm common jvm browser spec/conformance/freehand/fixtures/fh-react-005.edn active
FH-REACT-006 v/defhost is the SOLE public verb that mints Freehand's third legal vector head: a declaration produces one stable, qualified, NON-CALLABLE host descriptor that classifies as :host, a direct call raises :rf.error/view-called-directly rather than answering nil, the pre-door marked map is an ordinary map and an error head, and no other name on the door mints or names a host boundary — the runtime v/host constructor, the :kind split and v/react-el are rejected by name, each being the same crossing under a second spelling. :children and :ssr are required with no default, the option roster is closed, and a malformed declaration is :rf.ui.compile/bad-defhost at macro expansion 004-Views.md#qualified-host-leaves common jvm browser spec/conformance/freehand/fixtures/fh-react-006.edn active
FH-REACT-007 A host call splits into three DISJOINT planes: unreserved ordinary props (data, shallow, exact — an undeclared on*-looking name is ordinary data, never an inferred callback), the finite declared :callbacks positions each taking the matching v/event / v/handler carrier, and trailing children under the declared policy; an unfilled declared position is legal, while a bare event vector at a declared position, a wrong-role carrier, a function or a carrier in an ordinary slot, a caller-authored :children and a non-map props slot are each refused, and the children policy is reported through the same :rf.error/view-children-policy an internal boundary uses 004-Views.md#three-disjoint-planes common jvm browser spec/conformance/freehand/fixtures/fh-react-007.edn active
FH-REACT-008 A host crossing renders structurally as an honest marker — the declared host id, the declared SSR policy, the AUTHORED ordinary props with a carrier recording as its opaque role marker, a COUNT of the children that crossed into React's tree, and a :children slot carrying only the SSR projection (the declared fallback, or nothing) — never a host value, React element, function or third-party instance; a declared :map-props adapter is reported rather than having its output recorded; v/render-static refuses the crossing on the law it refuses v/behavior on; and a {:compiled true} parent mounting a host refuses at BUILD with :rf.ui.compile/host-crossing-unsupported naming the source and a recovery, rather than accepting the view or silently walking it interpreted 004-Views.md#structure-and-ssr common jvm browser spec/conformance/freehand/fixtures/fh-react-008.edn active

FH-ERROR — Errors

Boundary, reset, fallback, and safe-intent contract; private frame error egress; a failed candidate publishes nothing.

Id Law Canonical paragraph Applicability Fixture Status
FH-ERROR-001 An error boundary contains a render-class throw below it — the boundary shows its fallback while every sibling keeps rendering — and the failed candidate publishes nothing at all: the atomic bundle is absent, the cell owns no dependencies and carries no evidence 004-Views.md#containment-and-the-abandoned-candidate common jvm browser spec/conformance/freehand/fixtures/fh-error-001.edn active
FH-ERROR-002 A changed :reset-key by rf= clears the captured failure and re-mounts the child; under repeated failures within one generation the safe intent and the private egress fire exactly once, and a new failure generation after a reset reports exactly once again 004-Views.md#reset-and-the-once-per-generation-safe-intent common jvm browser spec/conformance/freehand/fixtures/fh-error-002.edn active
FH-ERROR-003 A caught render failure produces two representations by exact field set: a safe public summary an application receives, and a private egress record an off-box shipper receives that adds the opaque exception and a capped host stack — and neither carries app-db or event history 004-Views.md#the-safe-summary-and-the-private-frame-egress common jvm browser spec/conformance/freehand/fixtures/fh-error-003.edn active

FH-ROOT — Roots and SSR

Root Descriptor, preflight, identity, teardown, multi-root isolation, SSR emission, hydration.

Id Law Canonical paragraph Applicability Fixture Status
FH-ROOT-001 The minimal one-root mount (v/mount [app {…}] node) derives the root's identity from the mounted view's registered id (root-id and view-id that id, provenance :derived) and renders that declared view — the same root form putting real DOM on a browser page and answering one versioned structural tree on the JVM 004C-Roots-and-Mount.md#the-minimal-one-root-mount interpreted jvm browser spec/conformance/freehand/fixtures/fh-root-001.edn active
FH-ROOT-002 A root's occurrence identity is its derived root-id — the mounted view's qualified id, which a redefinition does not move — so a COMPATIBLE redefinition re-renders the existing host root through the boundary the host already mounted: the reloaded body renders while the mounted occurrence beneath it survives, proven by DOM state only a stable boundary can carry (an uncontrolled input's node, its typed value, its focus), with one cached boundary per view id and a body revision that advances at the reload seam; a redefinition whose hook skeleton moved is never served that boundary, and a re-mount that would drift the live root's effective identifierPrefix is refused with :rf.error/root-identifier-prefix-immutable before preflight and before React — the prefix claim and the committed render untouched — while the SAME prefix, authored or derived, stays the idempotent reload 004C-Roots-and-Mount.md#hot-reload-keeps-the-root-identity-and-the-mounted-occurrence interpreted browser spec/conformance/freehand/fixtures/fh-root-002.edn active
FH-ROOT-003 Two roots of ONE view on one page hold independent identity and independent state — distinguished by :disambiguator or an authored :root-id, each with its own host root, its own occurrence and its own injective identifierPrefix — and the three admission claims (id, container, prefix) each fail loud BEFORE any render, leaving every root already on the page rendering and untouched 004C-Roots-and-Mount.md#several-roots-on-one-page interpreted browser spec/conformance/freehand/fixtures/fh-root-003.edn active
FH-ROOT-004 A root's frame plan runs to completion BEFORE the host root exists — the frame is live and its initial events drained by the time the first render reads a subscription — the ENSURE and SCOPE spellings own and borrow the frame's lifetime respectively, and a plan meeting one frame under a different config fingerprint fails THAT root with :rf.error/frame-payload-conflict before install and before React, with the installed frame and its sibling roots untouched; and a same-root ENSURE takes the idempotent no-op or a surgical refresh only while it can PROVE it owns the incarnation live under the id, so an EQUAL fingerprint over a same-id successor is refused under :scope-config-less-or-own-the-lifetime — the still-live no-op and that refusal are the two halves pinned 004C-Roots-and-Mount.md#preflight-runs-before-react interpreted browser spec/conformance/freehand/fixtures/fh-root-004.edn active
FH-ROOT-005 Teardown is TOTAL by exact count: after v/unmount! the registry holds zero claims for that root, its container holds zero child nodes, the frame it ENSURED is destroyed and its ledger record gone, and the subscription its view held is released from the live sub-cache — while a frame the root merely SCOPED survives, and a stale or superseded handle is a guarded no-op rather than a teardown of the successor 004C-Roots-and-Mount.md#total-teardown interpreted browser spec/conformance/freehand/fixtures/fh-root-005.edn active
FH-ROOT-006 v/hydrate-root takes its root-id and its identifierPrefix from the Root Manifest the server emits as the container's immediately following element sibling — read from the wire, never derived — and ADOPTS the server-rendered markup, the server's own DOM nodes surviving the mount, while a divergence React recovers from is REPORTED as :rf.ssr/hydration-mismatch carrying that root-id, composed over any host :on-recoverable-error and bounded to the adoption window; identity opts supplied client-side are refused with :rf.error/root-manifest-invalid, server markup with no manifest adjacent is the same diagnostic with {:missing :manifest}, and no SSR artefact on the classpath is :rf.error/ssr-artefact-missing naming day8/re-frame2-ssr 011-SSR.md#hydration-on-the-freehand-paved-path interpreted browser spec/conformance/freehand/fixtures/fh-root-006.edn active
FH-ROOT-007 Manifest PRESENCE, not a non-empty container, is a hydrating root's server-render evidence: an empty container the server emitted for a view that rendered nothing still carries its Root Manifest and is ADOPTED under that identity, while a container with NO manifest adjacent takes the FALLBACK when it is empty — an ordinary client mount under the DERIVED identity, reported as the mount kind rather than as adoption errors and asking for no SSR artefact — and a container carrying markup that merely disagrees stays on the adoption path, reads its identity from the manifest, and takes the mismatch 011-SSR.md#the-fallback--a-container-with-nothing-to-adopt interpreted browser spec/conformance/freehand/fixtures/fh-root-007.edn active
FH-ROOT-008 An interpreted v/client-only site renders its MANDATORY capability-free fallback in :server phase — the structural render on either host, and a hydrating root's first render, marked :rf.ui/boundary :client-only and never entering the client subtree — and its client subtree in :client phase; a hydrating root flips :server:client exactly once, strictly AFTER the adoption commit, and that ONE root-scoped write swaps EVERY site in the root in a single update with no per-site state, while a non-hydrating mount is born :client and never flips 004-Views.md#client-only-subtrees interpreted jvm browser spec/conformance/freehand/fixtures/fh-root-008.edn active

The framework-supplied navigation anchor: a real element with a real href, the native-behaviour deferrals, the caller veto, the server shell, and the absent-routing diagnostic. The href and click law itself is routing's, cited here rather than restated.

Id Law Canonical paragraph Applicability Fixture Status
FH-ROUTELINK-001 v/route-link renders a real <a> whose href is the route's encoded URL, synthesised from :to plus :params / :query / :fragment; passthrough attributes reach the element and no route key leaks onto it 012-Routing.md#the-freehand-route-link-descriptor common jvm browser spec/conformance/freehand/fixtures/fh-routelink-001.edn active
FH-ROUTELINK-002 A modifier click, an auxiliary-button click, a :download anchor and a non-_self :target are NOT intercepted: the default action is left intact, nothing is dispatched, and the native attributes reach the DOM 012-Routing.md#the-freehand-route-link-descriptor common browser spec/conformance/freehand/fixtures/fh-routelink-002.edn active
FH-ROUTELINK-003 A caller-supplied :on-click runs before the framework's click decision and pre-empts it: if it prevents the default, the framework neither prevents nor dispatches 012-Routing.md#the-freehand-route-link-descriptor common browser spec/conformance/freehand/fixtures/fh-routelink-003.edn active
FH-ROUTELINK-004 The JVM/SSR render emits the same real anchor with the path-form href and no click handler 012-Routing.md#the-freehand-route-link-descriptor common jvm ssr spec/conformance/freehand/fixtures/fh-routelink-004.edn active
FH-ROUTELINK-005 Rendering without the routing artefact raises the named :rf.error/routing-artefact-missing at render — naming the view and the link's :to — rather than emitting a dead anchor 012-Routing.md#the-freehand-route-link-descriptor common jvm browser spec/conformance/freehand/fixtures/fh-routelink-005.edn active
FH-ROUTELINK-006 One common render produces the identical anchor on the JVM and in ClojureScript, the click closure being the only host divergence; the descriptor itself is an ordinary declared view 012-Routing.md#the-freehand-route-link-descriptor common jvm browser spec/conformance/freehand/fixtures/fh-routelink-006.edn active
FH-ROUTELINK-007 :on-click is the imperative pre-navigation seam and its grammar is closed: a plain function or a v/handler runs exactly once before the navigation decision and may veto; an event vector, an options map or a v/event is rejected at render on both hosts, naming the recovery 012-Routing.md#the-freehand-route-link-descriptor common jvm browser spec/conformance/freehand/fixtures/fh-routelink-007.edn active
FH-ROUTELINK-008 :prefetch is a routing control key stripped before DOM emission, and :prefetch :intent binds :on-mouse-enter / :on-focus / :on-touch-start to the routing prefetch for the link's OWN address — composing with a caller handler of the same name and narrowing those positions to the same closed callback roster :on-click carries — while a link that did not opt in binds none of them, a render dispatches nothing, and the server shell installs no intent handler 012-Routing.md#the-freehand-route-link-descriptor common jvm browser spec/conformance/freehand/fixtures/fh-routelink-008.edn active

FH-STRUCT — Structure

The versioned semantic tree, the conversion table, the explicit host policy.

Id Law Canonical paragraph Applicability Fixture Status
FH-STRUCT-001 The structural tree is plain serialisable data in five discriminated variants, discriminated in one pinned order; the root node carries the schema version and a value the closed node set cannot carry fails loud 004B-UI-Tree-and-Conversion.md#the-node-schema--version-1 interpreted jvm browser spec/conformance/freehand/fixtures/fh-struct-001.edn active
FH-STRUCT-002 An element node carries its tag verbatim with no case folding, .class#id sugar merged sugar-first, disjoint :attrs and :events, :key iff the site was keyed, :ns absent for HTML, and every optional field absent when empty 004B-UI-Tree-and-Conversion.md#element-fields-pinned interpreted jvm browser spec/conformance/freehand/fixtures/fh-struct-002.edn active
FH-STRUCT-003 Attribute values normalize into semantic space: one canonical class string, a canonical style map under the px rule, name for keywords and symbols, JavaScript ToString for numbers on both hosts, booleans intact, nil entries dropped 004B-UI-Tree-and-Conversion.md#attr-value-normalization-in-tree-semantic-space interpreted jvm browser spec/conformance/freehand/fixtures/fh-struct-003.edn active
FH-STRUCT-004 Children canonicalise into one document-order vector: nil/false/true drop, numbers become text, seqs and forwarded children runs flatten, adjacent text coalesces, empty strings drop, and :children is absent when empty except on a fragment 004B-UI-Tree-and-Conversion.md#child-normalization-canonical-form interpreted jvm browser spec/conformance/freehand/fixtures/fh-struct-004.edn active
FH-STRUCT-005 Namespace context is derived at render: HTML writes no :ns, <svg>/<math> carry the namespace they open, descendants inherit it, and <foreignObject> and an HTML-island <annotation-xml> revert their children 004B-UI-Tree-and-Conversion.md#namespaces interpreted jvm browser spec/conformance/freehand/fixtures/fh-struct-005.edn active
FH-STRUCT-006 The same declaration yields an equal structural tree on the JVM and in ClojureScript, with view boundaries as real nodes recording props, key, and the expansion — and the same declaration promoted with {:compiled true}, its call sites and this fixture untouched, yields that same tree 004B-UI-Tree-and-Conversion.md#cross-host-equality common jvm browser spec/conformance/freehand/fixtures/fh-struct-006.edn active
FH-STRUCT-007 The React emitter mounts a declared view as real DOM: elements, converted attribute names, composed classes, text, and a keyed run of child boundaries, read back off the document 004B-UI-Tree-and-Conversion.md#the-react-emitter interpreted browser spec/conformance/freehand/fixtures/fh-struct-007.edn active
FH-STRUCT-008 Numbers render with JavaScript Number::toString(10) on both hosts — shortest round-tripping digits, ties to even, ECMA's plain/exponential window — through text children, attribute values and CSS values alike, while a JVM integer beyond JavaScript's exactly representable range renders exactly and sits outside the cross-host claim 004B-UI-Tree-and-Conversion.md#attr-value-normalization-in-tree-semantic-space interpreted jvm browser spec/conformance/freehand/fixtures/fh-struct-008.edn active
FH-STRUCT-009 The React emitter spells attributes as React's canonical props at every depth and across declared-view boundaries, the structural tree keeps author-space names, the reserved and rejected spellings are refused by both emitters rather than reaching React, and an alias of an accepted slot-owning key is routed to that key's slot rather than beside it 004B-UI-Tree-and-Conversion.md#attribute-names interpreted jvm browser spec/conformance/freehand/fixtures/fh-struct-009.edn active
FH-STRUCT-010 A compiled declaration's manifest carries the finite subscription, event, slot, HTML-site, crossing and capability rosters its analysis makes statically knowable, each roster a function of the body's lexical sites and empty rather than absent when the body carries none, together with the capability set and the ViewCell-elision verdict those rosters decide; every entry of every one of those rosters carries a whole, positive {:file :line :column} source coordinate whenever a reader location survived to the declaration — the census subjects are written at top level and so are anchored, which makes this the TOTAL arm of the coordinate law, while the ABSENT arm (a wrapper-generated declaration the reader anchored nowhere, whose entries omit the key rather than carry a partial map) is proven by re-frame.freehand.manifest-total-or-absent-coord-jvm-test — and an :events entry carries beside it the per-site facts that say what the site dispatches — the authored handler :prop, its :classification, :serializable?, :sync? and the recorded :handler — proven over a per-roster census of declared cardinality so no roster's shape row is asserted over zero entries 004D-Freehand-Compiled-Grammar.md#the-finite-manifest compiled jvm browser spec/conformance/freehand/fixtures/fh-struct-010.edn active
FH-STRUCT-011 A v/custom-element declaration classifies a prop identically on every lowering path — a compiled literal props map, a compiled element's dynamic props, the interpreted walk, v/spread and v/spread-safe — because the classification belongs to the declaration rather than to the tier that saw the prop; a declared name keeps its authored kebab key and its value verbatim outside the attribute-value grammar, is named in the node's reserved property set, is omitted from server markup under every spelling, and is applied in the browser as the ruled kebab → camelCase JS property; the declaration is the sole classifier and it outranks handler position, so a declared :on-detail is a property on every path and an undeclared one is a native event on every path; while every other name is an attribute, an undeclared element is all-attributes, and declaration order is not an authoring decision 004B-UI-Tree-and-Conversion.md#custom-elements-per-the-ruled-grammar common jvm browser ssr spec/conformance/freehand/fixtures/fh-struct-011.edn active

FH-DIAG — Diagnostics

Versioned occurrence schema, stable ids, source and recovery, bounded retention, provable-only static findings.

Id Law Canonical paragraph Applicability Fixture Status
FH-DIAG-001 The checker analyses a declaration read-only against the versioned grammar and answers a stable report — view id, source, current lowering, target grammar, eligibility, and findings each carrying a stable id, source coordinates, the offending form, a reason, and a non-empty recovery ladder ending in :keep-interpreted 004D-Freehand-Compiled-Grammar.md#the-read-only-checker compiled jvm spec/conformance/freehand/fixtures/fh-diag-001.edn active
FH-DIAG-002 A compiled view proven to carry no reactive site omits the reactive ViewCell shell and one carrying a reactive site retains it; the verdict is a deterministic function of the analyzed sites, so the number of views that omit the shell over a census of known cardinality is an exact integer, never a threshold 004D-Freehand-Compiled-Grammar.md#the-capability-elision-verdict compiled jvm browser spec/conformance/freehand/fixtures/fh-diag-002.edn active
FH-DIAG-003 The manifest publishes the compile-tier finding roster — every finding the declaration produced in source order, the suppressed ones included, empty rather than absent when the checks found nothing — each entry carrying the finding id, the compiler-minted stable site id its warning quotes, and the tag and path of the node it was minted from, a suppressed entry carrying the author's reason and a reported one carrying none; a finding is located by that node rather than by a source coordinate, which is why it is governed here and not by the site rosters' coordinate law 004D-Freehand-Compiled-Grammar.md#compile-tier-warnings compiled jvm browser spec/conformance/freehand/fixtures/fh-diag-003.edn active