Skip to content

feat(gust): fused gust:os component — one component, five capabilities (gale#224), and what composing revealed - #235

Merged
avrabe merged 1 commit into
mainfrom
feat/fused-gustos-component
Jul 29, 2026
Merged

avrabe merged 1 commit into
mainfrom
feat/fused-gustos-component

Conversation

@avrabe

@avrabe avrabe commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Closes the structural half of gale#224 — and surfaces an architecture defect that native linking had been hiding.

The artifact

One component, five gust:os exports, residual imports exactly the hardware seam plus the app-supplied task poll:

world root {
  import gust:hal/mmio@0.1.0;
  import gust:os/taskdisp@0.1.0;

  export gust:os/time@0.1.0;
  export gust:os/log@0.1.0;
  export gust:os/spawn@0.1.0;
  export gust:os/exec@0.1.0;
  export gust:os/timer@0.1.0;
}

16 905 B of wasm. wasm-tools validate passes; no env, WASI, heap or scheduler symbol survives in any core import.

The finding: structurally fused, semantically triplicated

exec-provider, spawn-provider and timer-provider each #[path]-include the same plain/src/executor.rs, each with its own static mut TASKS. Composed, that is three separate component instances with three separate linear memories — the composite declares 5:

(memory (;0;) 17)   # time
(memory (;0;) 17)   # log
(memory (;0;) 1)    # spawn
(memory (;0;) 1)    # exec
(memory (;0;) 17)   # timer

So a handle from spawn.start() is meaningless to timer.sleep() or exec.state(). This is not one scheduler. The composite satisfies gale#224's stated invariant while not yet being a coherent OS, and saying only the first half would be an overclaim.

This is worth stating positively too: the duplication was invisible while everything was linked into one native image. Composing the OS as components made it structurally explicit — 5 memories, 3 schedulers, handles that don't travel. Composition worked as a diagnostic.

The fix is an architecture change — spawn and timer importing exec instead of embedding it — which would also give wac plug a real edge to connect. Deliberately not done here; no provider logic or WIT was changed.

wac plug is structurally inapplicable; this uses wac compose

The five providers form a flat antichain: each exports a capability, none consumes another's. There is no edge for plug. Every ordering fails with the socket component had no matching imports for the plugs that were provided. The correct operation is a union merge with import forwarding — wac compose over a WAC script — which worked first try. (The plan doc said "wac plug"; the artifact disagreed, and the artifact wins.)

Gates are not no-ops — negative-tested

The per-provider gate was widened (a provider may import gust:hal/* and gust:os/*, nothing else) and still rejects real leaks — env, wasi:io/poll, gale:heap/alloc. The fused gate is deliberately stricter: it allows gust:os/taskdisp exactly, not gust:os/*. Run against a deliberately partial 2-provider composite it fails on all four expected counts. Deny-list sweep catches env, wasi:io/poll, malloc, __heap_base, poll_task while correctly passing gust:hal/mmio and the WIT-typed poll-task.

Minor: a WIT world wider than reality

world timer-provider declares import taskdisp, but the built component imports only gust:hal/mmio — the timer path never reaches poll_task, so it is dropped at link. Reported, not changed.

Explicitly NOT claimed

Not lowered to native — no .o, no code size, no SRAM, no cycles; 16 905 B is a wasm number. Not executed anywhere: no hardware, no Renode, no qemu, no wasm runtime. Zero gust:os calls have run through this composite. Composing components proves nothing about their contents, and no new verification claim is made.

Tools: wasm-tools 1.245.1, wac 0.10.1.

Part of the v0.6.0-rc Research Day cut (M1).

🤖 Generated with Claude Code

https://claude.ai/code/session_011QG86sovTbfnPNY9SfhSmo

…e (gale#224)

One wasm component exporting all five gust:os capabilities (time, log, spawn,
exec, timer) whose only residual imports are gust:hal/mmio and the app-supplied
gust:os/taskdisp. 16905 B, validates, gated by build-fused-gustos.sh.

wac plug does not apply: the five providers are a flat antichain (no provider
consumes another's export), so every plug ordering exits "the socket component
had no matching imports for the plugs that were provided". The operation needed
is a union merge with import forwarding — wac compose over a WAC script.

build-gustos-components.sh now builds all five providers, and its per-component
gate is widened from "imports only gust:hal/*" to "imports only gust:hal/* or
gust:os/*" — exec-provider and spawn-provider legitimately import the trusted
gust:os/taskdisp dispatch, which the APPLICATION supplies, so it is expected to
survive composition. Negative-tested: env, wasi:*, and heap imports still fail.

Not lowered to native, not run anywhere, and the three embedded executor copies
remain three separate Tasks tables in three separate linear memories — the
composite is structurally fused but semantically triplicated. FUSED-GUSTOS.md
records all of this explicitly.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011QG86sovTbfnPNY9SfhSmo
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit 7f8e543 into main Jul 29, 2026
63 checks passed
@avrabe
avrabe deleted the feat/fused-gustos-component branch July 29, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant