Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

The Fera UI stack is an unconsumed island: no window layer, no render-tree producer

Источник: dev/fera-research/docs/oc2/fera-ui-stack-unconsumed-island.md (read-only; сгенерировано scripts/sync-docs.mjs)

The Fera UI stack is an unconsumed island: no window layer, no render-tree producer

Section titled “The Fera UI stack is an unconsumed island: no window layer, no render-tree producer”
  • kind: problem
  • area: ui
  • severity: high
  • evidence:
    • ~/dev/fera/fera/Cargo.toml:2-19 (workspace members — no window/renderer crate)
    • ~/dev/fera/fera/core/crates/ui-contract/Cargo.toml:13 (only fera-ui-engine-vello depends on it)
    • ~/dev/fera/fera/core/crates/ui-elm/Cargo.toml (zero dependents)
    • ~/dev/fera/fera/core/crates/ui-engine-vello/src/lower.rs:118-119 (“the actual surface is owned by the window layer”)
    • ~/dev/fera/fera/core/crates/ui-engine-vello/src/lower.rs:77-81 (Text/Image/Spacer silently skipped)
    • ~/dev/fera/fera/core/crates/ui-engine-vello/src/lib.rs:8 (“ADR-028 (Vello + Parley + winit + AccessKit substrate)”)
    • ~/dev/fera/fera/launcher/Cargo.toml (empty [dependencies]), launcher/src/main.rs (20 LOC)
    • ~/dev/fera/fera/tools/bench-scene/src/main.rs (15 LOC, prints "... generator skeleton")

Measured facts, whole-repo greps over ~/dev/fera/fera (excluding target/):

  1. Nothing in the workspace depends on any ui-* crate except one link. The only fera-ui-* dependency edge in the entire workspace is ui-engine-vello/Cargo.toml:13 → fera-ui-contract. fera-ui-elm has zero dependents.
  2. No crate ever constructs a RenderTree. grep -rn "RenderTree" --include=*.rs outside ui-contract/src and ui-engine-vello/src returns zero hits. The tree is a type that only its own module and its test module name.
  3. VelloEngine and ParleyTextEngine are never instantiated outside their own tests.
  4. There is no window layer. grep -rni "winit\|wgpu\|Surface\|swapchain\|create_window" over *.rs + *.toml matches only comments and doc strings — zero dependencies, zero code. Yet lower.rs:118-119 documents “the actual surface is owned by the window layer, so this only validates the dimensions”, and lib.rs:8 advertises “ADR-028 (Vello + Parley + winit + AccessKit substrate)”. The layer that ADR-028 names does not exist in this repo.
  5. The launcher is a 20-LOC binary with an empty [dependencies], while its own package description is “verifies + loads the core module per the signed trust manifest (ADR-013)”. It cannot load a module it does not depend on.
  6. tools/bench-scene is a 15-LOC main() that prints "fera-bench-scene: fera-scene-v0 generator skeleton" — while its own header comment declares the scene “FROZEN (OQ-006): 20 chats x 5k messages (100k total) … 50 concurrent 512x512@30fps stickers”. No fixture is emitted.

So the shape is: three well-documented crates (1836 LOC total) describing a seam, with no window at either end. The Engine trait has one implementation, zero callers.

A concrete defect inside the island: paint() cannot fail and drops text silently

Section titled “A concrete defect inside the island: paint() cannot fail and drops text silently”

ui-engine-vello/src/lower.rs:

fn lower(&mut self, tree: &RenderTree) { // :70 — returns ()
self.scene.reset();
for node in tree.nodes() {
match &node.kind {
NodeKind::PushClip(rect) => self.push_clip(*rect),
NodeKind::PopClip => self.scene.pop_layer(),
NodeKind::Rect { rect, fill } => self.fill_rect(*rect, *fill),
NodeKind::Text(_) | NodeKind::Image { .. } | NodeKind::Spacer(_) => {
// Text and images are lowered by the text/media
// passes; a text-less engine pass skips them
// rather than drawing placeholders.
} // :77-81
}
}
}

and

fn paint(&mut self, tree: &RenderTree, _size: Size) -> Result<(), Self::Error> { // :140
self.lower(tree);
Ok(()) // unconditional
}

Consequences:

  • paint’s doc comment claims “[VelloError::Render] if lowering fails” (:138-139) — but lower returns () and cannot fail. VelloError::Render is only ever constructed in resize (:127). The documented error is unreachable.
  • A tree containing only text, images and spacers lowers to a completely empty scene and still returns Ok(()). Under a Telegram workload (message list = text + avatars + spacers) that is the typical frame. Success is indistinguishable from “I drew nothing”, which is precisely the class of bug a golden-frame test exists to catch — and there are no golden-frame tests anywhere in fera (the 3 tests in lower.rs:159-189 only assert “did not panic”).
  • A. Accept the island as a deliberate spike and document it. Cheap, zero code. Cost: the doc comments that name ADR-028’s “winit + AccessKit substrate” and ADR-013’s “loads the core module” stay misleading, and any roadmap that assumes “the UI layer exists, we swap the engine” is wrong — both ends are missing.
  • B. Write the missing window layer first (winit + wgpu surface), then benchmark. Fixes the 3-LOC half of the island. Cost: pulls in winit/wgpu immediately, blocks on a vello version decision (see the vello-version-split finding), and produces a window that can only draw rectangles because text/images are skipped.
  • C. Fix the two cheap lies now, defer the window. Make lower/paint surface unhandled node kinds (error or counter), and correct the two doc comments. Cost: near-zero, kills the “success means nothing was drawn” trap.

C now, then A-as-stated. Two independent, cheap steps:

  1. paint must not claim to have drawn a frame it could not lower. Either lower returns Result and paint propagates a real VelloError::Render, or — cheaper and more honest — VelloEngine tracks an unsupported_kinds: u32 counter that paint reports. Given the workspace lints (clippy::todo/unimplemented = deny, unused_results = deny), the counter is the lower-friction option. This also gives the future golden-frame suite a natural invariant: frames are never silently empty.
  2. Fix the two doc claims. lower.rs:118-119 and lib.rs:8 name a window layer that does not exist in the repo. Either the window crate is tracked as a missing member of the workspace, or the comments stop referring to it as present-tense fact.

Then, separately, the integration question this raises is the real finding: chroix cannot be dropped into fera-ui-engine-vello as a new Engine impl, because chroix’s seam is inverted relative to fera’s (separate finding: seam-inversion). But given finding 1 above — nothing consumes Engine at all — the honest framing is that there is no integration cost to pay yet, and no integration pressure to justify either. The two projects are at the same maturity level and can be sequenced deliberately rather than urgently.