Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.{
.name = .stem,
.version = "0.14.0",
.version = "0.15.0-dev",
.fingerprint = 0x20bf92e5730ad4ee, // Changing this has security and trust implications.
.minimum_zig_version = "0.16.0",
.dependencies = .{
Expand All @@ -13,8 +13,8 @@
.hash = "vigil-3.0.1-0NlCVBMVCgAWMqsHUKLkbiCTNUnqBKOBEdYSHBBIN04E",
},
.wick = .{
.url = "git+https://github.com/ooyeku/wick#e1863b8398414a8cbefbceb56e411f9920d17696",
.hash = "wick-0.1.0-USl9A27VAQBXZpHJGamgAdIvdALivqpsfO1xLsrFrA9F",
.url = "git+https://github.com/ooyeku/wick?ref=v0.1.0#9c68751449683b27b1d4bc03412544b131f3f629",
.hash = "wick-0.1.0-USl9A3ljAwBiAPYTBkfg8R4ahLS1Ouk0Bwiic57arg7a",
},
.zls = .{
.url = "https://github.com/zigtools/zls/archive/refs/tags/0.16.0.tar.gz",
Expand Down
18 changes: 12 additions & 6 deletions docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,16 +394,22 @@ orchestrates both runtimes. Notable responsibilities:
### Wasm runtime (wick)

The interpreter is [wick](https://github.com/ooyeku/wick) — the
pure-Zig wasm interpreter extracted from this repo, pinned in
[build.zig.zon](../build.zig.zon). Loader and lifecycle live in
[src/plugins/wasm/loader.zig](../src/plugins/wasm/loader.zig); the
API/behavior contract between the two projects is wick's
`docs/stem-contract.md`.
pure-Zig wasm interpreter extracted from this repo, pinned by release
tag in [build.zig.zon](../build.zig.zon). Loader and lifecycle live in
[src/plugins/wasm/loader.zig](../src/plugins/wasm/loader.zig).

- Full wasm 1.0 coverage (i32/i64/f32/f64, funcref tables and
`call_indirect`) plus the bulk-memory `memory.init` / `data.drop`
opcodes (so plugins can ship passive data segments for static
strings).
strings). Function bodies are translated to a pre-decoded IR once at
load time and executed by a threaded-dispatch loop.
- **Entry points are signature-checked when they're resolved.** The
loader declares the ABI as Zig function types and asks wick to match
them against the module, so a plugin exporting `handle_command` with
the wrong shape fails with `SignatureMismatch` instead of being
called with the host's assumed arity and reading garbage locals.
`activate` may be either `() -> ()` or `() -> i32`; the rest of the
ABI is exact.
- Every plugin call runs under an instruction budget
(`CALL_FUEL_BUDGET`, 50M instructions, reset per call). A runaway
call fails with `OutOfFuel` instead of hanging the editor; host
Expand Down
187 changes: 111 additions & 76 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ with editors that have a twenty-year head start.

The plan closes stem's known gaps against mature terminal editors
(registers, system clipboard, macros, format robustness, plugin
authoring, battle-testing) across three releases, but each gap is
implemented *the stem way*: durable, supervised, and inspectable.
authoring, battle-testing), each implemented *the stem way*: durable,
supervised, and inspectable. Items have moved between releases and
say so where they did — a roadmap that quietly rewrites its own
history is the same kind of dishonest instrument as a counter that
can't report a drop.

---

Expand Down Expand Up @@ -42,32 +45,16 @@ Slipped to 0.14.0, where it shipped — see that release below.

### System clipboard that works everywhere (OSC-52)

Terminal editors live over SSH, inside tmux, on machines without a
display server. Exec-based clipboard bridges (`pbcopy`, `xclip`) fail
exactly where a terminal editor is most needed.

- OSC-52 copy integration with capability detection and graceful
fallback to the internal clipboard
- `pbcopy`/`wl-copy`/`xclip` bridge as a secondary path when available
- Clipboard state visible in the control center (which backend is
active, last sync result) — no silent "why didn't that copy?"

*Positioning check: dependable in hostile environments, with the
failure mode observable instead of mysterious.*
Did not ship. Tracked as
[#1](https://github.com/ooyeku/stem/issues/1) and rescheduled — see
0.15.0's deferral note, which batches it with the terminal
compatibility matrix since both need capability detection.

### Named registers with durable storage

Stem has a single unnamed clipboard. Mature modal editors have named
registers; stem's version makes them crash-safe.

- Vim-style named registers (`"a`–`"z`, append with `"A`–`"Z`) plus a
numbered yank ring
- Registers persist per project through the checkpoint pipeline —
yanked text survives a crash and a restart, which no incumbent offers
- Register picker in the command palette (inspect before you paste)

*Positioning check: aligned — registers become durable editor state
under the same recovery guarantees as sessions.*
Did not ship. Tracked as
[#3](https://github.com/ooyeku/stem/issues/3) and picked up in 0.15.0
below, where it also makes 0.14.0's macros durable.

---

Expand Down Expand Up @@ -126,74 +113,122 @@ the message bus, which makes them better than a keystroke tape:
*Positioning check: aligned — "all-or-nothing replay" is a reliability
claim no incumbent macro system makes.*

### Deterministic simulation testing

Vigil ships a deterministic toolkit (`SimulatedClock`,
`SimulatedTimerService`, `FaultInjector`); stem uses only a corner of
it. This release makes time-dependent behavior provable:

- Debounce, backoff, breaker, and watchdog logic tested against the
simulated clock — no sleeps, no flakes
- Fault-injection tests for the LSP lifecycle: scripted crash storms
must open breakers, recover on schedule, and never lose a queued
`didOpen`
### Did not ship: the "provable behavior" half

### Battle-testing, phase one
0.14.0 was themed *repeatable operations and provable behavior*. It
shipped the repeatable half. Recorded here rather than quietly rolled
forward, because the gap is the reason 0.15.0 looks the way it does:

- Expand the fuzz corpus beyond piece-table/state/URIs to the session
format, LSP framing, and plugin manifests
- Unicode robustness pass over cursor motion, rendering width, and
text objects (grapheme clusters, combining marks, East Asian width)
- Begin a terminal compatibility matrix (kitty, alacritty, wezterm,
tmux, iTerm2, Terminal.app, Linux console) with documented results
- **Deterministic simulation testing.** Vigil ships a deterministic
toolkit (`SimulatedClock`, `SimulatedTimerService`, `FaultInjector`)
and stem still uses one corner of it — a single test exercising
vigil's own timer service. Stem's debounce, backoff, breaker, and
watchdog logic remains untested against a simulated clock.
- **Fuzz corpus expansion.** A wasm-loader target landed; the session
format, LSP framing, and plugin manifests did not.
- **Unicode robustness pass** and the **terminal compatibility
matrix** ([#5](https://github.com/ooyeku/stem/issues/5)) — not
started.

*Positioning check: aligned — a reliability claim obligates proof,
not vibes.*
All four carry into 0.15.0.

---

## 0.15.0 — Proven Under Fire

Theme: hardening completed, and the plugin host becomes the most
dependable extension surface in the terminal.
Theme: verification you can trust. Not the runtime's honesty about
itself — that shipped — but stem's honesty about *stem*.

The motivating discovery came during 0.14.0's release cleanup. Zig only
runs tests from files reachable from a test root, so a module can carry
a full suite that never executes and nothing complains. Nine modules
were in that state, `split_manager` — window splits — among them. Wiring
them back in didn't just surface failures; it surfaced test doubles that
had drifted out of sync with the interfaces they stand in for,
assertions that asserted the opposite of real behavior, and a
leak-checking helper that could only pass for code that allocated
nothing.

That is the same failure as the dishonest drop counter in the
[architecture notes](architecture.md) — a number that couldn't report
the thing it claimed to measure. Stem tells that story as a success.
This release applies the lesson to stem's own verification.

### Trustworthy test wiring

- A build step that walks `src/`, finds every file containing a `test`
block, and fails the build when one isn't reachable from a test root.
Cheap, mechanical, and it permanently closes the hole above.
- Audit the surviving test doubles against the interfaces they double;
the drift found so far was caught by accident, not by design.

### Plugin host v1: reliability as the ecosystem strategy
*Positioning check: aligned — "N tests pass" has to be a claim, not a
number.*

Stem cannot out-plugin Neovim by volume. It can be the host where a
plugin crash is a contained, observable, recoverable event — and where
authoring is low-friction:
### Deterministic simulation testing (carried from 0.14.0)

- **API stability contract**: manifest and SDK surface frozen for the
1.x line; breaking changes gated behind manifest versions
- `stem plugin new` scaffolding (wasm and exec templates, SDK wired)
- Supervised restart policies exposed per plugin (max restarts,
backoff, disable-on-poison) with breaker state in the dashboard
- A curated plugin index — small, but every entry vetted to run under
supervision without dead-lettering
- Debounce, backoff, breaker, and watchdog logic tested against
`SimulatedClock` — no sleeps, no flakes
- Fault-injection tests for the LSP lifecycle: scripted crash storms
must open breakers, recover on schedule, and never lose a queued
`didOpen`

*Positioning check: scrutinized hard. "Grow an ecosystem" chases the
incumbents on their terms and was cut; "the host that never lets a
plugin take the editor down" is the differentiated version of the same
gap.*
This is foundational for the next item: chaos runs can't gate merges
while the tests underneath them are timing-dependent.

### Battle-testing, phase two
### Chaos CI as a merge gate

- Chaos CI: fault-injection runs (killed LSPs, wedged plugins, full
queues, clock jumps) as a merge gate, built on the 0.14 simulation
harness
- Terminal compatibility matrix completed and published in the README
- Soak testing: multi-hour editing sessions under memory-leak and
file-descriptor tracking, with the runtime cockpit's own metrics as
the oracle
- Fault-injection runs — killed LSPs, wedged plugins, full queues,
clock jumps — built on the simulation harness above
- Fuzz corpus extended to the session format (now `std.json`, so the
round-trip is property-testable), LSP framing, and plugin manifests
- Soak testing: multi-hour sessions under memory and file-descriptor
tracking, with the runtime cockpit's own metrics as the oracle

### Cluster follow-through (stretch)
### Named registers with a yank ring

`STEM_CLUSTER` presence shipped in 0.13. If the foundation proves
stable, the first user-visible payoff: shared registers/clipboard
across local stem instances via the distributed registry.
The largest remaining modal-editing gap
([#3](https://github.com/ooyeku/stem/issues/3)), and it completes what
0.14.0 started: macros currently live in per-session registers, so
durable registers are what make "record a macro, crash, replay it"
true.

*Positioning check: aligned, and gated — ships only if presence
telemetry from 0.13–0.14 shows the transport is dependable.*
- Vim-style named registers (`"a`–`"z`, append with `"A`–`"Z`) plus a
numbered yank ring
- Persisted per project through the checkpoint pipeline, under the same
recovery guarantees as sessions
- Register picker in the command palette — inspect before you paste

*Positioning check: aligned — durable editor state under the recovery
guarantees the rest of stem already makes.*

### Deferred out of this release

Judgment calls worth stating, not silent omissions:

- **Plugin host v1 / API stability contract.** Freezing the manifest
and SDK surface for the 1.x line is the right destination, but the
plugin ABI changed shape in 0.14.0 (entry points are now
signature-checked at resolve time). Freeze after chaos CI has
stressed the host, not before.
- **A curated plugin index.** Needs an ecosystem that doesn't exist
yet.
- **OSC-52 clipboard** ([#1](https://github.com/ooyeku/stem/issues/1))
and the **terminal compatibility matrix**
([#5](https://github.com/ooyeku/stem/issues/5)). Both need terminal
capability detection; batching them into one cycle avoids building
that twice. Ship them together, here if there's room and in 0.16.0
otherwise.
- **Cluster follow-through.** `STEM_CLUSTER` presence shipped in
0.13.0 and the payoff — shared registers across local instances —
stays gated on telemetry showing the transport is dependable. No
such evidence yet.

### If the schedule tightens

Cut the registers and ship a purely-hardening release. For a project
that sells reliability, the wiring guard plus chaos CI is a defensible
0.15.0 on its own.

---

Expand Down
Loading
Loading