Add native UI decision and testing workflow
CI / rust (push) Successful in 32s

This commit is contained in:
2026-08-31 15:05:20 -07:00
parent 29ffed1206
commit a703b5a5ee
13 changed files with 365 additions and 5 deletions
+3
View File
@@ -0,0 +1,3 @@
[alias]
xtest = "nextest run --workspace --all-features"
lint = "clippy --workspace --all-targets --all-features -- -D warnings"
+10
View File
@@ -0,0 +1,10 @@
[profile.default]
fail-fast = false
failure-output = "immediate-final"
slow-timeout = { period = "30s", terminate-after = 2 }
[profile.ci]
fail-fast = false
retries = 0
status-level = "pass"
final-status-level = "slow"
slow-timeout = { period = "30s", terminate-after = 2 }
+27
View File
@@ -0,0 +1,27 @@
name: CI
on:
push:
pull_request:
jobs:
rust:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pinned Rust toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.94.1
components: rustfmt, clippy
- name: Format
run: cargo fmt --all --check
- name: Lint
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
- name: Test
run: cargo test --workspace --all-features
+4 -3
View File
@@ -37,8 +37,9 @@ workspace's `Research/` directory. They are references, not vendored code.
Before committing Rust changes, run: Before committing Rust changes, run:
```bash ```bash
cargo fmt --all --check ./scripts/ci.sh
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
``` ```
Keep `bacon` running during development. Tests must use synthetic fixtures; real
agent transcripts, subscription state, private source, and credentials are never
test data.
+8 -2
View File
@@ -37,11 +37,17 @@ contracts before committing to a large implementation.
Start with [the product spec](docs/PRODUCT_SPEC.md), Start with [the product spec](docs/PRODUCT_SPEC.md),
[architecture](docs/ARCHITECTURE.md), [research map](docs/RESEARCH.md), and [architecture](docs/ARCHITECTURE.md), [research map](docs/RESEARCH.md), and
[captured upstream revisions](docs/RESEARCH_SNAPSHOTS.md). [captured upstream revisions](docs/RESEARCH_SNAPSHOTS.md). The native UI choice
is intentionally open; see [the measured decision plan](docs/UI_OPTIONS.md).
## Scaffold ## Scaffold
```bash ```bash
cargo test --workspace bacon # continuous check; t tests, c lints, v runs the full gate
cargo xtest # fast isolated tests with nextest
./scripts/ci.sh # format + strict Clippy + tests + doctests
cargo run -p lumbridge cargo run -p lumbridge
``` ```
See [the testing strategy](docs/TESTING.md) for fake harnesses, ACP replay,
terminal conformance, UI driving, recovery, performance, and packaging tests.
+45
View File
@@ -0,0 +1,45 @@
#:schema https://dystroy.org/bacon/.bacon.schema.json
default_job = "check"
env.CARGO_TERM_COLOR = "always"
[jobs.check]
command = ["cargo", "check", "--workspace", "--all-targets", "--all-features"]
need_stdout = false
[jobs.clippy]
command = [
"cargo", "clippy", "--workspace", "--all-targets", "--all-features",
"--", "-D", "warnings",
]
need_stdout = false
[jobs.test]
command = [
"cargo", "nextest", "run", "--workspace", "--all-features",
"--hide-progress-bar", "--failure-output", "final",
]
need_stdout = true
analyzer = "nextest"
[jobs.doc-test]
command = ["cargo", "test", "--workspace", "--all-features", "--doc"]
need_stdout = true
[jobs.run]
command = ["cargo", "run", "-p", "lumbridge"]
need_stdout = true
allow_warnings = false
background = true
show_command_error_code = true
[jobs.ci]
command = ["./scripts/ci.sh"]
need_stdout = true
[keybindings]
c = "job:clippy"
t = "job:test"
d = "job:doc-test"
r = "job:run"
v = "job:ci"
@@ -0,0 +1,10 @@
use lumbridge_core::{Platform, ProductStatus, UsageProvenance};
#[test]
fn downstream_crates_can_use_the_public_scaffold_contract() {
let status = ProductStatus::architecture_phase(Platform::ArchLinux);
assert_eq!(status.platform.to_string(), "Arch Linux / Omarchy");
assert!(status.message.contains("Architecture scaffold"));
assert_eq!(UsageProvenance::Estimated, UsageProvenance::Estimated);
}
+110
View File
@@ -0,0 +1,110 @@
# Testing strategy
Lumbridge controls shells, repositories, credentials, agents, and long-lived
processes. A test plan built only from UI clicks would be slow, flaky, and blind
to the failures that matter. The product is designed with programmatic testing
surfaces from the start.
## The central seam
The desktop UI and the test driver use the same versioned local IPC commands and
events exposed by the session runtime. UI tests can therefore:
1. start an isolated runtime with a temporary data directory;
2. issue real commands such as create workspace, split pane, launch harness,
resize PTY, approve tool, cancel turn, and restore session;
3. wait on semantic events rather than sleeps;
4. query a redacted state snapshot; and
5. separately confirm the visible/accessibility representation where needed.
The test API is not a back door. It uses the same validation and state machine as
the UI, binds locally, requires a per-run capability token, and is absent from
release builds unless explicitly enabled.
## Test layers
### Pure and property tests
Layouts, terminal grid transitions, usage windows, forecasts, event reduction,
path rules, and capability negotiation are deterministic functions. Test them
exhaustively without starting a window. Property tests should target split-tree
invariants, event replay, Unicode width, and usage aggregation.
### Contract tests
Every platform adapter implements the same suites for PTYs, process groups,
signals, resize, secret handles, notifications, paths, and atomic replacement.
Every harness adapter implements discovery, launch, resume, cancel, status, and
usage contracts. Unsupported capability is a valid explicit result.
### Fake harnesses
Small Rust fixture binaries behave like coding agents without network access or
subscriptions. Scripts cover partial frames, noisy stderr, permission prompts,
child processes, crashes, hangs, huge output, invalid bytes, and cancellation.
Tests assert both user-visible state and cleanup of the entire process tree.
### ACP replay
Synthetic ACP transcripts are replayed in both directions against the official
Rust SDK. Golden fixtures cover protocol versions and capability combinations.
Fuzzing targets frame decoding and state transitions; stdout must remain protocol
pure and diagnostics must stay on stderr.
### Terminal conformance
Byte-stream fixtures assert screen cells, styles, cursor, alternate screen,
scrollback, selection, hyperlinks, bracketed paste, keyboard modes, and Unicode.
PTY integration tests exercise real shells on each supported OS. Human typing
and agent automation are stressed concurrently to catch input-order corruption.
### Persistence and recovery
The runtime is interrupted after each durable event and external mutation, then
restarted. Tests prove accepted commands are not lost, incomplete work is
reconciled, migrations are forward-only, and corrupt/truncated tails fail safely.
### UI and rendering
Component tests drive semantic actions and accessibility identifiers. Golden
images cover a small set of high-value layouts at fixed fonts, scale, theme, and
GPU/software renderer. Snapshot updates require review; snapshots never replace
state assertions.
### Performance gates
Criterion and end-to-end probes record input-to-present latency, output ingest,
scrollback search, six-pane frame time, idle CPU, memory per pane, startup, and
restore. CI uses generous regression ceilings; dedicated metal/macOS runs retain
the detailed distributions.
### Packaging smoke tests
Fresh macOS, Ubuntu, and Omarchy/Arch environments install, launch, open a shell,
upgrade, roll back, and uninstall. Artifacts are checked for signatures,
checksums, SBOM, license notices, forbidden secrets, and source revision.
## Developer loop
```bash
# Continuous cargo check. Press t for nextest, c for strict Clippy, v for all.
bacon
# Direct fast test run.
cargo xtest
# Exactly what must pass before a commit.
./scripts/ci.sh
```
`cargo-watch` is no longer the project default because upstream archived it in
2025. `scripts/watch.sh` uses Bacon and retains a cargo-watch fallback for people
who already have the final cargo-watch release installed.
## CI matrix as the project grows
Every change runs format, Clippy, unit, contract, integration, and doctests on
Linux. Pull requests that touch platform/UI code also run macOS. Nightly jobs add
MSRV, coverage, dependency/license audit, fuzz smoke, renderer snapshots, soak,
and performance tracking. Release candidates must pass native packaging smoke on
macOS, Ubuntu, and Arch/Omarchy rather than treating cross-compilation as proof.
+96
View File
@@ -0,0 +1,96 @@
# Native UI decision
Lumbridge will not use Electron, React, TypeScript, or a webview as its
application shell. The shell, layout engine, terminal, session runtime, and
editor surfaces must be native Rust. An isolated browser pane may host the
operating system web engine because rendering arbitrary web pages is a browser
problem, not an application-shell architecture.
This decision stays open until the finalists run the same workload. Choosing on
repository popularity or a polished demo would hide the risks that matter for a
terminal-heavy, accessibility-aware, cross-platform IDE.
## Finalists
### GPUI
GPUI is the closest architectural match to Zed: retained state, GPU rendering,
text-heavy native interfaces, and an Apache-2.0 framework crate. It is the
highest-upside choice if Lumbridge wants Zed-like responsiveness and is willing
to track a fast-moving framework.
Risks:
- it is pre-1.0 and tightly shaped by Zed's needs;
- framework documentation and third-party component coverage are still young;
- the complete dependency and license closure must be audited before adoption;
- Zed's editor, terminal, and Markdown application crates are GPL-3.0-or-later,
so Lumbridge cannot copy those implementations into an Apache-2.0 release.
### Floem
Floem is the strongest independent comparator. It is MIT-licensed, pure Rust,
fine-grained and reactive, renders through wgpu/Vello or Skia, and is proven in
the Lapce editor. Its editor primitives may shorten the path to a native
Markdown/code surface without binding Lumbridge to Zed's application code.
Risks:
- it is also pre-1.0 and has a smaller ecosystem;
- current releases carry custom windowing integration that needs maintenance
review;
- accessibility, IME, embedded-browser parenting, and multi-window behavior
still need validation on every target desktop.
## Viable fallback
Iced is MIT-licensed, cross-platform, and renderer-agnostic with a wgpu path. It
has approachable architecture and broad examples, but its own documentation
still calls it experimental. It is a sound fallback if GPUI and Floem fail the
spike, though Lumbridge would build more IDE/editor infrastructure itself.
## Useful building blocks, not the shell decision
- `winit + wgpu + cosmic-text` gives maximum renderer control and is a sensible
direction for a dedicated terminal canvas. Owning an entire desktop toolkit,
editor, IME, accessibility layer, menus, drag-and-drop, and window integration
in the first release would be too much product risk.
- `egui` is excellent for internal diagnostics and rapid prototypes. Its
immediate-mode model and non-native visual conventions make it a weaker main
IDE shell.
- `wry` can host a tightly isolated system-web-engine child for browser panes:
WKWebView on macOS and WebKitGTK on Linux. It must not become a web frontend or
a privileged bridge into the session runtime.
- `AccessKit` should provide the semantic accessibility tree regardless of the
rendering framework.
The Markdown experience remains native: a rope-backed editor, incremental
parsing and syntax trees, and a Rust Markdown renderer. Preview links and
embedded web content cross the browser boundary through a narrow, capability-
checked interface.
## The decision spike
Build the same disposable shell in GPUI and Floem before product implementation:
- workspace sidebar, tab strip, split tree, command palette, footer, and six
fake busy terminal panes;
- native Markdown edit and preview surface;
- browser placeholder followed by one isolated system-web-engine child;
- semantic test identifiers, keyboard-only navigation, accessibility tree, IME,
copy/paste, drag-and-drop, and window restore;
- package and launch on macOS, Ubuntu, and Omarchy/Arch.
Record, do not estimate:
- cold and warm startup;
- idle RSS and RSS with six panes;
- key-to-present p50/p95 and output-to-present latency;
- frame time while all panes stream output;
- binary and installed size;
- accessibility/IME correctness and native window behavior;
- build/package friction and the complete dependency/license closure.
Choose the framework whose measured result gives Lumbridge the best input and
streaming latency without failing accessibility, packaging, or maintainability.
If the results are close, prefer the smaller operational and licensing risk.
+4
View File
@@ -0,0 +1,4 @@
[toolchain]
channel = "1.94.1"
components = ["clippy", "rustfmt"]
profile = "minimal"
Executable
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
cargo fmt --all --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
if command -v cargo-nextest >/dev/null 2>&1; then
cargo nextest run --workspace --all-features --profile "${NEXTEST_PROFILE:-default}"
else
echo "cargo-nextest not installed; using cargo test" >&2
cargo test --workspace --all-features
fi
# nextest deliberately does not run doctests on stable Rust.
cargo test --workspace --all-features --doc
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
if command -v bacon >/dev/null 2>&1; then
exec bacon "$@"
fi
# Compatibility for developers who still have the now-dormant cargo-watch.
if command -v cargo-watch >/dev/null 2>&1; then
exec cargo watch -x "check --workspace --all-targets --all-features" \
-x "test --workspace --all-features"
fi
echo "Install Bacon with: cargo install --locked bacon" >&2
exit 127
+18
View File
@@ -0,0 +1,18 @@
# Deterministic fixtures
Fixtures in this directory are synthetic and safe to publish. They must never be
copied from real subscription transcripts, private repositories, shell history,
or credentials.
Planned fixture families:
- `harness/`: scripted child processes covering stdout, stderr, exit, signals,
prompts, long lines, invalid UTF-8, backpressure, and process-tree cleanup.
- `acp/`: versioned initialize/session/prompt/cancel transcripts and capability
combinations, including malformed and out-of-order frames.
- `terminal/`: compact VT byte streams with expected screen and scrollback state.
- `usage/`: provider-, harness-, measured-, and estimated-usage observations.
- `recovery/`: event logs ending at every externally visible interruption point.
Large generated corpora belong in release artifacts or a dedicated test-data
repository, not in normal Git history.