diff --git a/README.md b/README.md index 08c9093..90fb8d5 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,9 @@ standing on its own. Run the proof: `cargo run -p counter-cli`. Next (see `docs/ARCHITECTURE.md` § roadmap O3–O6): -- `positron-ratatui` — terminal renderer reference impl (O3) -- `positron-lit` — Lit DOM reference renderer + regenerate `@positron/core` (O4) +- `positron-ratatui` — terminal renderer + `Host` event loop (O3, outlier A) +- `positron-wgpu` — one Rust GPU renderer for native (Metal/Vulkan/DX12) + web (WebGPU/WASM) + AR/VR (O4, outlier B — "web ≠ DOM") +- `positron-lit` *(optional)* — Lit DOM renderer for a11y / text-reflow (O4b) - `ContinuumHost` (in continuum) — session ↔ Commands/Events, first real `ViewState` (O5) - persona `Observer` → RAG/tool bridge (O6) - Theme pack (Loki / Matrix / Fallout / Tron) ported from the cyberpunk-cli experiment diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 576a419..c34fbce 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -27,11 +27,17 @@ not rewriting the widget. acts via CommandEnvelope ``` +> The boxes illustrate the **role** — every surface is a `Renderer` projection +> of the same `ViewState`. They are *not* the primary-renderer choice: as +> § *Renderer paradigms are GPU-first* (below) makes precise, one Rust wgpu +> renderer covers web + native + AR/VR, so "DOM" and "native" are not separate +> surfaces but two of that one renderer's targets. + The four "environments" collapse to **two roles**: | Role | Consumes | Produces | Surfaces | |---|---|---|---| -| `Renderer` | the `ViewState` | a surface tree | web (Lit DOM), terminal (ratatui), mobile (native), AR/VR (Bevy) | +| `Renderer` | the `ViewState` | a surface (GPU frame / cells / DOM) | **wgpu GPU frame** — one Rust renderer running native (Metal / Vulkan / DX12), web (WebGPU, WebGL fallback, via WASM), and AR/VR (Bevy is wgpu underneath); terminal (ratatui); optional DOM (Lit, a11y/text-reflow) | | `Observer` | the **same** `ViewState` | perception → RAG, then `CommandEnvelope`s | AI persona | The AI persona is **not a special case**. It is the fourth projection of the @@ -39,6 +45,34 @@ identical `ViewState` — it perceives what a human sees, and it acts through th same command vocabulary a human's host emits. That is the positron principle, made structural. +### Renderer paradigms are GPU-first — "web" ≠ "the DOM" + +`Renderer`'s `type Output` is renderer-specific, so nothing in the contract +assumes a CPU text tree or a DOM. That is what lets the **primary** surface be a +single Rust **wgpu** GPU renderer that compiles to *every* paradigm from one +codebase: + +| Paradigm | How the one wgpu renderer reaches it | +|---|---| +| Native desktop | wgpu → Metal (macOS) / Vulkan (Linux) / DX12 (Windows) | +| Web | wgpu → WebGPU (WebGL fallback), shipped as WASM — **same source** | +| AR/VR | Bevy, which is wgpu underneath | +| Terminal | `positron-ratatui` — CPU cells, a genuinely different `Output` | +| DOM | `positron-lit` — optional a11y / text-reflow surface, not the primary web story | + +The DOM (Lit) and terminal (ratatui) are **peer paradigms**, not the definition +of "web" and "native." This makes the GPU renderer the real **outlier B** for +validating `Renderer`: `counter-cli`'s `String` vs `Vec` are both CPU +text (a weak outlier pair); a GPU draw-list/frame is maximally different and +proves the trait carries no hidden CPU-tree assumption. + +This also means positron does **not** invent a new GPU path — it *converges* +with one continuum already has: continuum's avatar renderer is Bevy/wgpu emitting +backend-neutral `RgbaFrame`s over a crossbeam channel that LiveKit and PNG +consume without knowing Bevy (a `RenderBackend` seam). `SceneDescription`/ +`RgbaFrame` (continuum) is the same shape as `ViewState`/frame (positron); the +two reconcile at **O5**, not by rewriting either. + ## Separation of concerns — the layer boundary (the load-bearing part) Positron **owns**: @@ -99,7 +133,7 @@ does not carry. | Concern | Home | Why | |---|---|---| | The four traits + wire + session | **positron** (`positron-core`) | consumer-agnostic contract; independently versioned (the "airc test") | -| Reference renderer crates (`positron-ratatui`, `positron-lit`) | **positron** | surface projections anyone in the problem domain reuses | +| Reference renderer crates (`positron-ratatui`, `positron-wgpu`, `positron-lit`) | **positron** | surface projections anyone in the problem domain reuses | | Concrete `ViewState` types (`ChatViewState`, …) | **continuum** | domain vocabulary — positron never knows what a "chat message" is | | Command vocabulary (360+ commands) | **continuum** | already the single source of truth; positron frames, does not define | | `ContinuumHost` adapter (session ↔ Commands/Events) | **continuum** | binds the contract to *this* substrate's primitives | @@ -134,9 +168,10 @@ the view from `ViewState` alone, the state type is incomplete — not the render is the recommended default rather than a primitive-enforced necessity; DESIGN.md leans the same way. - **`@positron/core` (npm) + `positron-lit` vs continuum `sdk/typescript`: decided - at O4, not before.** Both generate types from Rust via ts-rs; whether the positron - web stack *subsumes* or *complements* the continuum SDK is resolved when the web - renderer actually lands. + at O4b, not before.** Both generate types from Rust via ts-rs; whether the positron + DOM stack *subsumes* or *complements* the continuum SDK is resolved when the Lit + renderer actually lands. (O4 is `positron-wgpu`, the GPU surface — it has no bearing + on the npm/SDK reconcile, which is the DOM stack's concern.) ## Organizational task roadmap (one PR per unit) @@ -144,11 +179,15 @@ the view from `ViewState` alone, the state type is incomplete — not the render |---|---|---|---| | **O1** | This separation contract | the boundary is pinned before any renderer is written | — | | **O2** | `examples/counter-cli` | renderer-agnostic **and** AI-perceives-same-state, in-process, zero transport (one `Counter` `ViewState`, ≥2 `Renderer`s, 1 `Observer`) | O1 | -| **O3** | `positron-ratatui` | terminal `Renderer` reference (outlier A surface) | O2 | -| **O4** | `positron-lit` | web DOM `Renderer` + regenerate `@positron/core`; reconcile with continuum `sdk/typescript` (subsume vs complement) | O2 | -| **O5** | `ContinuumHost` (in continuum) | positron session ↔ Commands/Events; first real `ViewState` (`ChatViewState`) flows to a positron renderer; resolves the two-wire merge question | O3 or O4 | +| **O3** | `positron-ratatui` | terminal `Renderer` reference + `Host` event loop (**outlier A**: CPU cells, real stateful surface) | O2 | +| **O4** | `positron-wgpu` | the run-everywhere GPU `Renderer` (**outlier B**: native Metal/Vulkan/DX12 + web WebGPU/WASM from one codebase) — proves `Renderer` carries no CPU-tree assumption; the load-bearing web+native surface | O2 | +| **O4b** | `positron-lit` *(optional)* | Lit DOM `Renderer` + regenerate `@positron/core`; a11y / text-reflow surface where the GPU path isn't ideal; reconcile with continuum `sdk/typescript` (subsume vs complement) | O4 | +| **O5** | `ContinuumHost` (in continuum) | positron session ↔ Commands/Events; first real `ViewState` (`ChatViewState`) flows to a positron renderer; reconciles positron's frame output with continuum's existing `RenderBackend`/`RgbaFrame` GPU seam; resolves the two-wire merge question | O3 or O4 | | **O6** | persona `Observer` → RAG/tool bridge (in continuum) | perception into cognition + action as `CommandEnvelope` — closes "AI persona rag/tool integration" | O5 | -O2 is the next unit: it makes this doc non-vapor by proving "one `ViewState`, -many renderers, plus an observer perceiving the same state" in a single process, -before any transport or substrate is involved. +O1 and O2 have landed: the boundary is pinned, and `examples/counter-cli` proves +"one `ViewState`, many renderers, plus an observer perceiving the same state" in +a single process, before any transport or substrate. **O3 is the next unit** — +`positron-ratatui`, the first real stateful `Renderer` + `Host` event loop +(outlier A), ahead of the GPU renderer (O4, outlier B) that makes the "web ≠ DOM" +claim real.