# wity-rkf A declarative format for reactive, interactive knowledge documents as state machines. Headless, renderer-agnostic, zero dependencies. f(document, state) -> ComputedFrame. The spec defines element types and evaluation semantics; the reference implementation serializes as XML. Runs in browser and Node.js. Current version: rkf-core v2.0.0 Docs: https://www.wity.ai/stack/reactive-knowledge-format/ ## Architecture ``` @wity-ai/rkf-core -- parse, evaluate, serialize, validate, expression evaluator. Zero DOM. Zero dependencies. Isomorphic. XML is the current serialization; the spec is encoding-independent. ``` Core model: an RKF document is a pure function of state. Call `evaluate(doc, state)` with any state at any time. No mutation, no side effects. Documents are modelled as state machines grounded in three vector spaces: - **Feature-space** (rkf-var) -- dimensions of the machine's state - **Observation-space** (rkf-gate) -- named boolean conditions over features - **Transformation-space** (rkf-transform) -- state transitions triggered by gates State is emergent, not declared -- it arises from regions in feature-space recognised by gates. ## Document structure ``` RkfDocument ├── RkfVar[] (document-level feature variables) └── RkfSection[] (top-level sections; optional gate) ├── RkfEnv (opaque environments: prose, math, image, video, model, audio) └── RkfMachine (state machine blocks) ├── RkfVar[] (feature-space: state dimensions) ├── RkfGate[] (observation-space: named conditions) ├── RkfDerive[] (derived features) ├── RkfTransform[] (transformation-space: state transitions) │ └── RkfSet[] (variable assignments) ├── RkfPort[] (typed I/O interfaces, dir=in|out) ├── RkfMachine[] (nested sub-machines, recursive) ├── RkfCanvas (visual output area) │ ├── RkfScale[] (data-to-pixel mapping) │ ├── RkfAxis[] (axis descriptors) │ └── Marks[] (scatter, line, arrow, rect, circle, arc, text, path, area) └── RkfEnv[] (opaque environments with optional ports) ``` ## Quick start ```js import * as graph from '@wity/graph-headless'; import { evaluate, initState, serialize, parse, setGraphProvider } from '@wity-ai/rkf-core'; // An RKF document is a plain object: the model parse() returns and serialize() takes. const doc = { version: '2.0', id: 'demo', title: 'Hello RKF', vars: [{ tag: 'rkf-var', id: 'x', type: 'number', init: 0.5 }], sections: [{ tag: 'rkf-section', id: 'intro', children: [ { tag: 'rkf-env', type: 'prose', content: 'Move the slider to change the value.' }, { tag: 'rkf-machine', id: 'viz', vars: [], gates: [], transforms: [], machines: [], derives: [{ tag: 'rkf-derive', id: 'y', expr: 'x * x' }], ports: [{ tag: 'rkf-port', dir: 'in', var: 'x', type: 'continuous', min: 0, max: 1, step: 0.01, label: 'x' }], children: [{ tag: 'rkf-canvas', width: 400, height: 300, children: [{ tag: 'rkf-circle', cx: 'x', cy: 'y', r: 8, fill: '#3b82f6' }], }], }, ], }], }; const state = initState(doc); // { x: 0.5 } const frame = evaluate(doc, state); // ComputedFrame for (const mark of frame.marks) { console.log(mark.tag, mark.cx, mark.cy, mark.r); // rkf-circle 0.5 0.25 8 } // Stored form: a graph. parse() / serialize() need graph-headless, injected once // (in Node.js also setXmlParser(new DOMParser())). setGraphProvider(graph); const xml = serialize(doc); // … const same = parse(xml); // the same document ``` ## Reactive loop ```js let state = initState(doc); function onChange(varName, value) { state = { ...state, [varName]: value }; const frame = evaluate(doc, state); render(frame); // your renderer } function onTick() { state = applyUpdates(transforms, state, 'tick'); const frame = evaluate(doc, state); render(frame); } ``` ## Composition An RKF environment can contain heterogeneous content blocks, including embed blocks that host other `` representations: ```js { kind: 'embed', application: 'workbook', snapshot: { ... } } { kind: 'embed', application: 'system', snapshot: { ... } } ``` Supported applications: `workbook`, `system`, `decision-matrix`, `swot`, `eisenhower`, `ooda`. The snapshot is opaque to rkf-core — the renderer mounts the appropriate domain editor. This makes RKF a composite document format: a single document can contain prose, math, reactive state machines, live spreadsheets, and system maps. ## Expression evaluator Safe, hand-rolled recursive descent. No eval(), no Function(). Supports: arithmetic (+, -, *, /, %), comparison (==, !=, <, >, <=, >=), logical (&&, ||, !), ternary (? :), parentheses, Math.* functions, dot access. ## Editing `RkfEdit` (exported from `@wity-ai/rkf-core`) is the headless edit API over the document model: sections (add, rename, remove), cards (add, move, resize, style, split by heading), blocks inside a card (insert, update, move, remove; rows with cells), links between cards, drawing marks, and machines (vars, gates, derives, transforms, ports — checked before anything changes). Everything is addressed by a stable id. Functions mutate in place and return what a caller records; undo, events and rendering belong to the caller. --- ## Documentation pages ### Guides - [Overview](/guide/overview) -- core model, document structure, three-space model, state model - [Schema v2.0](/guide/schema) -- full XML attribute reference for all element types - [Composition](/guide/composition) -- how RKF embeds workbooks, system maps, and frameworks inline - [Rendering](/guide/rendering) -- SVG, Canvas 2D, DOM renderer examples, reactive loop ### API Reference - [@wity-ai/rkf-core](/packages/rkf-core) -- parse, evaluate, initState, applyUpdate, serialize, validate, evalExpr, evalGate - [RkfEdit](/packages/rkf-edit) -- the edit API: sections, cards, blocks, links, drawing marks, machines