add architecture docs synced to current source and sdd process

Phase 1 of SDD process: syncing docs/architecture/ to reflect the
existing source code. Eight component documents describe WHAT and WHY
(not HOW) for each module: schema, element factory, reactive layer,
host config, transforms, events, pointers, and build distribution.
Three ADRs capture key decisions (HTML-agnostic core, TypeBox Module
as type registry, Preact signals-core for reactivity). Each doc
documents known reconciler gaps and references the research in
docs/research/reconciler/.

Also adds docs/sdd_process.md (process reference shared across
alkdev projects) matching the taskgraph_ts pattern.
This commit is contained in:
2026-05-18 15:00:33 +00:00
parent 497a01c544
commit 09f32f0c64
13 changed files with 2072 additions and 0 deletions

126
docs/architecture/README.md Normal file
View File

@@ -0,0 +1,126 @@
---
status: draft
last_updated: 2026-05-18
---
# @alkdev/ujsx Architecture
Universal JSX — runtime-agnostic reactive tree primitives with TypeBox schemas. UJSX treats JSX as an intermediate representation for multi-target rendering.
## Why This Exists
UJSX fills a specific niche: **generic tree construction and rendering** where the same declarative template can target different hosts (markdown, graph structures, UI frameworks, workflow engines). It borrows the JSX mental model — nested elements with props and children — but strips away all platform-specific assumptions.
No `onClick`, no `className`, no `style`. The tree is a pure data structure (`UNode`) validated by TypeBox schemas, and the rendering contract is a HostConfig that decides what "create", "update", and "remove" mean for its target.
This makes UJSX useful for:
- **Workflow definitions** — operations as elements, dependencies as parent-child structure, rendered to graphology DAGs or reactive execution engines
- **Structured document generation** — markdown, HTML, or any text format via transform rules
- **Desktop UI** — instanced glyphs, panels, layouts rendered to Three.js or similar (the spoke UI use case)
## Core Principle
**The tree is the truth. Hosts are interpreters.** UJSX defines what a tree looks like (`UNode`, `UElement`, `URoot`), how it's constructed (`h()`, `createComponent()`), and how it can react to changes (signals). It does not dictate what the tree means — that's the host's job.
## Current State
UJSX is functional but incomplete. The core primitives exist and are tested:
- **Schema** — TypeBox Module (`UJSX`) defining `UNode`, `UElement`, `URoot`, `UPrimitive`, `PropValue`, `UniversalProps`; TypeScript types `ComponentFn` and `UComponent`; type guards `isUElement`, `isURoot`, `isUPrimitive`
- **Element factory** — `h()`, `createRoot()`, `createComponent()`, `Fragment`, JSX runtime
- **Reactive layer** — `ReactiveRoot`, `reactiveComponent`, `reactiveElement`, signal/computed/effect/batch
- **HostConfig** — generic host interface with `createRoot().render()` for mount-only rendering
- **Transforms** — `TransformRegistry` for bi-directional transforms
- **Events** — `PubSubLike` and `EventEnvelope` for decoupled event emission
- **Pointers** — `ValuePointer`, `selectNode`, `setNode` for tree navigation and targeted mutation
**Known gaps** (to be addressed by the reconciler work documented in `docs/research/reconciler/`):
- `unmount()` is a stub — no fiber tree teardown, no instance removal, no signal disposal
- `render()` is mount-only — no re-render, no diffing, no `prepareUpdate`/`commitUpdate` calls
- `dispose` functions are no-ops — signal subscriptions leak
- No `key` field on `UElement` — positional matching only
## Architecture Documents
| Document | Content |
|----------|---------|
| [schema.md](schema.md) | TypeBox Module, UNode/UElement/URoot/UPrimitive types, type guards |
| [element-factory.md](element-factory.md) | h(), createRoot(), createComponent(), Fragment, JSX runtime |
| [reactive-layer.md](reactive-layer.md) | ReactiveRoot, reactiveComponent, reactiveElement, signals, disposal gaps |
| [host-config.md](host-config.md) | HostConfig interface, createRoot(), mount-only rendering, reconciler gap |
| [transforms.md](transforms.md) | TransformRegistry, TransformRule, TransformContext, bi-directional transforms |
| [events.md](events.md) | EventEnvelope, PubSubLike, UjsxEventMap |
| [pointers.md](pointers.md) | ValuePointer, selectNode, setNode, tree navigation |
| [build-distribution.md](build-distribution.md) | Package structure, exports map, dependencies, runtime targets |
### Design Decisions
| ADR | Decision |
|-----|----------|
| [001](decisions/001-html-agnostic-core.md) | HTML-agnostic core — no DOM-specific props |
| [002](decisions/002-typebox-module-as-registry.md) | TypeBox Module IS the type registry |
| [003](decisions/003-preact-signals-for-reactivity.md) | Preact signals-core for reactivity |
## Consumer Context
UJSX is designed as a library consumed by other projects, not an end-user application. Understanding these consumers shapes the API design:
### Flowgraph (`@alkdev/flowgraph`)
Uses UJSX as a direct dependency. Workflow templates are `UNode` trees. Renders them to:
- **graphology DAG** — structural analysis, cycle detection, topological sort via a `HostConfig`
- **Reactive execution engine** — runtime workflow execution with signal-based status propagation
See `docs/research/reconciler/05-flowgraph-host-configs.md` for the planned integration.
### Desktop UI (Spoke HUD)
Uses UJSX to define instanced glyph layouts, panels, and adaptive-density content. Renders via a Three.js `HostConfig`. Signal-driven property updates flow through `prepareUpdate`/`commitUpdate` to GPU buffers.
### OpenCode Plugin (future)
An OpenCode plugin that provides UJSX-based template operations. Would use TransformRegistry for bi-directional markdown↔UJSX conversion.
## Reconciler Roadmap
The reconciler research in `docs/research/reconciler/` documents a phased plan to close the current gaps:
| Phase | Description | Status |
|-------|-------------|--------|
| 0 | `key` field on `UElement` | Research complete |
| 1 | Reactive → Host bridge (fiber tree, signal-driven updates) | Research complete |
| 2 | Key-based children reconciliation (LIS algorithm) | Research complete |
| 3 | Unmount & dispose support | Research complete |
| 4 | TypeBox value optimization layer | Research complete |
| 5 | Flowgraph HostConfig implementations | Research complete |
Research docs are in `docs/research/reconciler/`. Architecture docs for the reconciler will be created during the architecture phase of the SDD process, informed by this research.
## Document Lifecycle
Architecture documents use YAML frontmatter with `status` and `last_updated` fields:
```yaml
---
status: draft | stable | deprecated
last_updated: YYYY-MM-DD
---
```
| Status | Meaning | Transitions |
|--------|---------|-------------|
| `draft` | Under active development. Content may change significantly. Implementation should not start until the document reaches `stable`. | → `stable` when implementation is complete and API contract is verified by tests. |
| `stable` | API contracts are locked. Changes require a review cycle and may warrant an ADR if they affect documented decisions. | → `deprecated` when superseded. → `draft` if a fundamental redesign is needed (rare). |
| `deprecated` | Superseded by another document. Kept for reference. Links should point to the replacement. | Removed when no longer referenced. |
ADR documents use a separate `Status` field in their body: `Proposed`, `Accepted`, `Deprecated`, or `Superseded`. ADRs never revert from `Accepted`.
## References
- UJSX research index: `docs/research/README.md`
- Reconciler research: `docs/research/reconciler/`
- SDD process: `docs/sdd_process.md`
- Preact signals-core: `@preact/signals-core`
- TypeBox: `@alkdev/typebox`
- Taskgraph_ts architecture pattern: `/workspace/@alkdev/taskgraph_ts/docs/architecture/`