WAI Studio on the web: one front end, two hosts (the core in WebAssembly; wai dev at /_wai/), actions from the operation catalogue with a drift check #522

Merged
dcharlot merged 12 commits from wai/studio-web into main 2026-10-28 08:00:49 -04:00
Owner

WAI Studio on the web, v1 (build order step 7, "Studio web at /studio/ and /_wai/"): one front end in wai/wai-studio/, Apache-2.0, with two hosts chosen when the page starts.

  • hosted: the core in WebAssembly, in the tab; nothing uploaded, no account. The site branch studio-v1 (WAI site repository) hosts it at /studio/.
  • local: served by wai dev at /_wai/ on the origin it already serves on loopback; the core in WebAssembly does what a tab can, and the local wai the rest, reached through the dev stack's own routes (/_wai/api/).

Operation names are provisional until #506 lands. Studio reads the catalogue from wai/catalogue/ops.json once it exists on main; until then from wai/wai-studio/ops.pending.json, a byte-for-byte copy of #506's export at 270be911a. When #506 merges I merge origin/main, delete the copy (the drift check fails until it is gone) and regenerate.

The panels, and which host shows each

Panel Hosted Through wai dev
Inspect yes: envelope, capabilities with their registry rows, provenance (refines, derived_from, security), SPEC §4 verdict at this sink, receipts when the index is opened with it yes, and the local wai's own wai inspect report
Verify and receipts yes: receipt indexes, groups, claims, checked in the tab by the verifier wai verify runs yes
Encode, decode, package yes: the floor (decode PNG, FLAC, zstd; encode PNG, zstd) and wrap of any registered capability yes, and every codec the local wai holds
Rooms yes: room.show, join as a viewer (the room client in a frame), leave; a pre-call test of a relay yes, against the dev room
Run no (the study's table: —) yes: the stack, every service and its loopback address, service.check, session.show, logs followed
Quantum yes: Quantum Studio, unchanged, as a panel yes, from Studio's build
Command palette every catalogue operation by id; one the host does not run says where it runs same

How the actions are generated, and the drift check

scripts/actions.mjs writes src/actions.generated.js from the catalogue export (every operation: id, menu/item, palette name, summary, placement, effect, CLI, MCP tool, route, the noun_verb call name). src/bindings.js binds by hand the operations Studio runs: panel, and per host tab / local / tab+local / page / none. --check fails on:

  • an action with no operation (a binding, a core call, or a wai dev LOCAL_OPS entry whose id the catalogue lacks);
  • an operation placed client or both with no binding (the catalogue has no "browser" placement; a page is a client of the service it names, so client and both are what a browser runs);
  • a mismatched name: the generated file not what the catalogue generates; a tab binding whose core call is not noun_verb; a core call or LOCAL_OPS entry bound to another host; an op id in the source (data-op, op:, .op() that names no operation.

What it found on this tree: nothing. 92 operations read (ops.pending.json), 15 actions bound: 13 run on a host (envelope.inspect, envelope.unwrap, registry.list, receipt.verify, media.decode, media.encode, envelope.wrap, room.show, room.join, room.leave, dev.start, service.check, session.show), 2 browser-placed operations bound to the command line (http.load, receipt.fetch). The other 77 are in the palette with where they run, from the catalogue.

The site branch

studio-v1 in the WAI site repository (pushed, not merged, not deployed), head 5803bb4: /studio/ mounts Studio's hosted host on the site's own room service, relay, room client and Quantum Studio; scripts/sync-wai-studio.mjs builds Studio at one commit into public/studio/ the way sync-room.mjs copies the room client (strict, each file's SHA-256 in BUILD.json), with room.html the room client's own page at the commit /room comes from (noindex: the frame's alone); the core is a studio row of scripts/wasm-pkgs.tsv, built reproducibly by build-wasm-pkg.sh (no local path in it; the same bytes from a745de9ba and 1f1d5f0ad). Synced at 1f1d5f0ad; re-sync at the merge commit before a deploy. On the Studio host: npm run build (routes, public text with the name denylist, device names) ok; e2e/studio.e2e.mjs every check passed; e2e/pages.e2e.mjs /studio/ /quantum-studio/ 307 passed, 0 failed. Its Quantum axe check leaves out #qs-critique, and says so: the site's Quantum Studio build (defc8c6f8) names it without a role; this PR names it a group (quantum-studio/src/critique-view.js), and the site has that when it next syncs Quantum Studio.

Decisions

  • Stack: Quantum Studio's. Plain ES modules built by esbuild, no framework, Playwright for the browser runs; the same pane, segment, button, badge and table idioms (ws- in place of qs-), and the site's design tokens copied as Quantum Studio copies them. No new visual idiom.
  • The core is a crate of its own, wai/wai-studio/core (wai-studio-core), not a feature of wai-web: the wai crate with floor, http and receipt_claims only, bound for WebAssembly (2 MB). Each binding is named noun_verb after its operation and answers the command line's JSON envelope (envelope/1). The command line's inspect, wrap and verify wording lives in the wai binary, not the library, so the core states the same checks (dispatch, pins, roles, candidates, framing, check_index) in its own JSON; folding the binary's text onto shared library functions waits for #506, which rewrites wai.rs.
  • "Browser placement" = client or both. wai.ops/1 has no browser placement; a page is a client of the service it names. Today those are http.load, receipt.fetch, room.join, room.leave, session.show. The first two are bound elsewhere: cli (the palette says wai http bench / wai http receipts); the check holds that the catalogue gives them a command line.
  • The local core is the dev stack's own routes, /_wai/api/ (state, log, sessions, op/<id>), on the origin wai dev already serves: no listener of its own. An operation runs this wai (its current executable) on the bytes the page sends, in .wai/dev/studio/op-*, removed after; one at a time, 120 s, 64 MiB in and out. Every API request must name the origin as its Host (no DNS rebinding), carry X-WAI-Studio: 1 (a cross-origin page cannot without a preflight this origin never answers), and state no other Origin or Sec-Fetch-Site. Until #506 the local wai prints text, so the page shows its report; after it, --json gives the envelope.
  • No new wai dev flag. Studio's files come from $WAI_STUDIO or share/wai/studio beside the binary. A --studio DIR flag would change dev.start's catalogue entry while #506 holds it.
  • Rooms run the room client itself, in a frame: its own page (/room/ under wai dev; on the site /studio/room.html, the client's own index.html at the commit /room comes from, its script pointed at /room/room-client.js), entered by its own Watch button once it reports idle, left by its Leave. Nothing of the client is rewritten.
  • The pre-call test is a Rooms control, not a palette action: room.test is in the study but not in #506's table, and an action with no operation fails the drift check. Add room.test (placement client) to the catalogue and the check will then require Studio to bind it.
  • Quantum is Quantum Studio unchanged: the panel makes the #studio mount element (data-embedded) and imports its module, which mounts itself. On the site it is the /quantum-studio/ build; under wai dev, Studio's build carries it (build.mjs --quantum).
  • One line of Quantum Studio changed: its critique's container (#qs-critique) carried aria-label with no role, which axe refuses (aria-prohibited-attr); it is now role="group". Nothing else of it is touched.
  • A viewer needs someone in the room: the room's clock opens its events track with the first participant, and a viewer that subscribes to it before then is refused no publisher within RENDEZVOUS_TIMEOUT. The e2e puts a publisher in the room first (the room page in Chromium with its fake camera and microphone), and Studio shows the room client's own error when a join fails.
  • wai dev --check checks Studio too (the page at /_wai/, and the stack's state through the API) when its files are found.
  • Two build findings: workspaces that build the wai crate with different features must not share a cargo target directory (on the Studio host wai's bin linked another workspace's libwai and failed; e2e/run.sh gives each workspace its own); and the floor's zstd is C, so on macOS its wasm32 archive needs llvm-ar (AR_wasm32_unknown_unknown): macOS's ar writes an archive with no index the linker can read.

Checks

Check Host Command Result
Studio e2e, Chromium + Firefox, both hosts Studio host (macOS), at 1f1d5f0ad (the head's code; the head changes one job name) bash wai/wai-studio/e2e/run.sh --browsers chromium,firefox every check passed (165 PASS: 82 a browser, and the room's publisher)
Studio e2e, WebKit, both hosts Framework box: builds on the host, node phase in the browser image of Playwright 1.60.0, at 27a6e7796 (the head) STUDIO_PHASES=build bash wai/wai-studio/e2e/run.sh, then STUDIO_PHASES=node … --browsers webkit in the image every check passed (83 PASS: 82 WebKit, and the room's publisher)
Unit tests (generation, every drift rule broken in a copy, palette, local core, each panel's core calls on the built wasm) both of the above node --test --test-reporter=tap test/*.test.mjs with WAI_STUDIO_CORE_PKG # pass 19, # fail 0, # skipped 0
Drift check both, and locally node scripts/actions.mjs --check Studio's actions hold to the catalogue
Core tests Framework cd wai/wai-studio/core && cargo test --locked 10 passed
Core clippy, native and wasm32 Framework cargo clippy --locked --all-targets -- -D warnings; --target wasm32-unknown-unknown exit 0, both
Core lock Framework cargo metadata --locked --offline --format-version 1 current (seeded from wai-rs/Cargo.lock: every version is one wai-rs already locks)
wai binary tests, cli_studio, cli_dx, floor Framework cargo test --locked --no-default-features --features quickstart --bin wai --test cli_studio --test cli_dx --test floor 25 + 1 + 2 + 3 passed
clippy on the touched wai files Framework cargo clippy --locked --no-default-features --features quickstart --bin wai --test cli_studio; and --no-default-features --bin wai no note on dx/dev.rs, dx/studio.rs, tests/cli_studio.rs
wai-rs lock Framework cargo metadata --locked --offline current, unchanged
repo checks local check-tests-in-ci.py, check-workflow-concurrency.sh, check-spec-refs.py, check-wai-section-refs.py, check-no-vanity-metrics.sh, test-public-text-checks.sh, check-comparative-prose.sh, check-orphan-sources.py, YAML parse of every workflow all clean
public names local check-public-names.sh on every changed file; commit messages and this body against the same patterns clean
after merging origin/main local wfsteps.py HEAD <base> HEAD^1 HEAD^2 1 problem: the quickstart step of wai-conformance.yml, which both sides changed; resolved as the union (--test cli_fleet --test cli_studio), diff against main is exactly this PR's lines

Falsifications (committed first; each mutation an explicit edit whose anchor matched once, run in an rb checkout)

Drift check (Studio host, node scripts/actions.mjs --check; unmutated exit 0, restored exit 0):

Mutation Exit What it said
D1 an action renamed: envelope.inspect bound as envelope.inspekt 1 the action envelope.inspekt has no operation in the catalogue (and its core call and server entry no longer match)
D2 an operation dropped from the catalogue: room.join removed from ops.pending.json 1 actions.generated.js is not what the catalogue generates; the action room.join has no operation; rooms.js names no operation
D3 an operation placed for the browser left with no action: room.leave unbound 1 room.leave is placed client and has no action
D4 a mismatched name: the core's envelope_inspect renamed envelope_read 1 the core has no call envelope_inspect; envelope_read names envelope.read, which the catalogue does not hold
D5 the generated table edited by hand ("menu":"Rooms") 1 not what the catalogue generates
D6 wai dev's LOCAL_OPS runs media.decode as media.unpack 1 LOCAL_OPS runs media.unpack, which the catalogue does not hold

/_wai/ and loopback (Framework, cargo test --test cli_studio; unmutated ok, restored ok):

Mutation Exit Where it failed
M1 wai dev's origin bound to 0.0.0.0 101 the origin is not loopback (cli_studio.rs:144)
M2 Studio opens a listener of its own on 0.0.0.0 101 "wai dev (the origin)" listens on 0.0.0.0:…, beyond loopback
M3 /_wai/ not routed to Studio 101 /_wai/ answers with Studio's page

Not built, and why

  • The release archive does not carry Studio yet (share/wai/studio), so wai dev from the quickstart archive serves /_wai/ only once Studio's files are given (WAI_STUDIO). The archive (wai/quickstart/build.sh, build-web.sh, wai-release.yml) is step 7's bundle, which #506 also edits; building the core into it needs Node and wasm-bindgen in the release job.
  • The study's other panels: Projects, Conformance, Keys, Create, and the Cloud views. Not in v1's brief.
  • room.test, wai inspect --json and --studio DIR wait on #506 (see Decisions).
  • The local core runs inspect, decode and encode; verify, wrap and unwrap run in the tab (the same library), so the local core adds nothing for them yet.
  • WebKit on the Studio host: it never launches from an SSH session there (nor from the SSH-started macOS runner), so it runs on Linux in Playwright's image, here on the Framework box and in CI's webkit · linux job.
  • Not deployed: the site branch studio-v1 is pushed for review (WAI site repository); its /studio/ serves this PR's head and must be re-synced at the merge commit before a deploy.

🤖 Generated with Claude Code

WAI Studio on the web, v1 (build order step 7, "Studio web at `/studio/` and `/_wai/`"): one front end in `wai/wai-studio/`, Apache-2.0, with two hosts chosen when the page starts. - **hosted**: the core in WebAssembly, in the tab; nothing uploaded, no account. The site branch `studio-v1` (WAI site repository) hosts it at `/studio/`. - **local**: served by `wai dev` at `/_wai/` on the origin it already serves on loopback; the core in WebAssembly does what a tab can, and the local `wai` the rest, reached through the dev stack's own routes (`/_wai/api/`). **Operation names are provisional until #506 lands.** Studio reads the catalogue from `wai/catalogue/ops.json` once it exists on main; until then from `wai/wai-studio/ops.pending.json`, a byte-for-byte copy of #506's export at `270be911a`. When #506 merges I merge `origin/main`, delete the copy (the drift check fails until it is gone) and regenerate. ## The panels, and which host shows each | Panel | Hosted | Through `wai dev` | |---|---|---| | Inspect | yes: envelope, capabilities with their registry rows, provenance (`refines`, `derived_from`, `security`), SPEC §4 verdict at this sink, receipts when the index is opened with it | yes, and the local `wai`'s own `wai inspect` report | | Verify and receipts | yes: receipt indexes, groups, claims, checked in the tab by the verifier `wai verify` runs | yes | | Encode, decode, package | yes: the floor (decode PNG, FLAC, zstd; encode PNG, zstd) and `wrap` of any registered capability | yes, and every codec the local `wai` holds | | Rooms | yes: `room.show`, join as a viewer (the room client in a frame), leave; a pre-call test of a relay | yes, against the dev room | | Run | no (the study's table: —) | yes: the stack, every service and its loopback address, `service.check`, `session.show`, logs followed | | Quantum | yes: Quantum Studio, unchanged, as a panel | yes, from Studio's build | | Command palette | every catalogue operation by id; one the host does not run says where it runs | same | ## How the actions are generated, and the drift check `scripts/actions.mjs` writes `src/actions.generated.js` from the catalogue export (every operation: id, menu/item, palette name, summary, placement, effect, CLI, MCP tool, route, the `noun_verb` call name). `src/bindings.js` binds by hand the operations Studio runs: panel, and per host `tab` / `local` / `tab+local` / `page` / none. `--check` fails on: - an action with no operation (a binding, a core call, or a `wai dev` LOCAL_OPS entry whose id the catalogue lacks); - an operation placed `client` or `both` with no binding (the catalogue has no "browser" placement; a page is a client of the service it names, so `client` and `both` are what a browser runs); - a mismatched name: the generated file not what the catalogue generates; a tab binding whose core call is not `noun_verb`; a core call or LOCAL_OPS entry bound to another host; an op id in the source (`data-op`, `op:`, `.op(`) that names no operation. What it found on this tree: nothing. 92 operations read (`ops.pending.json`), 15 actions bound: 13 run on a host (`envelope.inspect`, `envelope.unwrap`, `registry.list`, `receipt.verify`, `media.decode`, `media.encode`, `envelope.wrap`, `room.show`, `room.join`, `room.leave`, `dev.start`, `service.check`, `session.show`), 2 browser-placed operations bound to the command line (`http.load`, `receipt.fetch`). The other 77 are in the palette with where they run, from the catalogue. ## The site branch `studio-v1` in the WAI site repository (pushed, not merged, not deployed), head `5803bb4`: `/studio/` mounts Studio's hosted host on the site's own room service, relay, room client and Quantum Studio; `scripts/sync-wai-studio.mjs` builds Studio at one commit into `public/studio/` the way `sync-room.mjs` copies the room client (strict, each file's SHA-256 in `BUILD.json`), with `room.html` the room client's own page at the commit `/room` comes from (noindex: the frame's alone); the core is a `studio` row of `scripts/wasm-pkgs.tsv`, built reproducibly by `build-wasm-pkg.sh` (no local path in it; the same bytes from `a745de9ba` and `1f1d5f0ad`). Synced at `1f1d5f0ad`; re-sync at the merge commit before a deploy. On the Studio host: `npm run build` (routes, public text with the name denylist, device names) ok; `e2e/studio.e2e.mjs` every check passed; `e2e/pages.e2e.mjs /studio/ /quantum-studio/` 307 passed, 0 failed. Its Quantum axe check leaves out `#qs-critique`, and says so: the site's Quantum Studio build (`defc8c6f8`) names it without a role; this PR names it a group (`quantum-studio/src/critique-view.js`), and the site has that when it next syncs Quantum Studio. ## Decisions - **Stack: Quantum Studio's.** Plain ES modules built by esbuild, no framework, Playwright for the browser runs; the same pane, segment, button, badge and table idioms (`ws-` in place of `qs-`), and the site's design tokens copied as Quantum Studio copies them. No new visual idiom. - **The core is a crate of its own, `wai/wai-studio/core` (`wai-studio-core`),** not a feature of `wai-web`: the `wai` crate with `floor`, `http` and `receipt_claims` only, bound for WebAssembly (2 MB). Each binding is named `noun_verb` after its operation and answers the command line's JSON envelope (`envelope/1`). The command line's inspect, wrap and verify wording lives in the `wai` binary, not the library, so the core states the same checks (dispatch, pins, roles, candidates, framing, `check_index`) in its own JSON; folding the binary's text onto shared library functions waits for #506, which rewrites `wai.rs`. - **"Browser placement" = `client` or `both`.** `wai.ops/1` has no browser placement; a page is a client of the service it names. Today those are `http.load`, `receipt.fetch`, `room.join`, `room.leave`, `session.show`. The first two are bound `elsewhere: cli` (the palette says `wai http bench` / `wai http receipts`); the check holds that the catalogue gives them a command line. - **The local core is the dev stack's own routes, `/_wai/api/`** (`state`, `log`, `sessions`, `op/<id>`), on the origin `wai dev` already serves: no listener of its own. An operation runs this `wai` (its current executable) on the bytes the page sends, in `.wai/dev/studio/op-*`, removed after; one at a time, 120 s, 64 MiB in and out. Every API request must name the origin as its Host (no DNS rebinding), carry `X-WAI-Studio: 1` (a cross-origin page cannot without a preflight this origin never answers), and state no other Origin or `Sec-Fetch-Site`. Until #506 the local `wai` prints text, so the page shows its report; after it, `--json` gives the envelope. - **No new `wai dev` flag.** Studio's files come from `$WAI_STUDIO` or `share/wai/studio` beside the binary. A `--studio DIR` flag would change `dev.start`'s catalogue entry while #506 holds it. - **Rooms run the room client itself,** in a frame: its own page (`/room/` under `wai dev`; on the site `/studio/room.html`, the client's own `index.html` at the commit `/room` comes from, its script pointed at `/room/room-client.js`), entered by its own Watch button once it reports `idle`, left by its Leave. Nothing of the client is rewritten. - **The pre-call test is a Rooms control, not a palette action:** `room.test` is in the study but not in #506's table, and an action with no operation fails the drift check. Add `room.test` (placement `client`) to the catalogue and the check will then require Studio to bind it. - **Quantum is Quantum Studio unchanged:** the panel makes the `#studio` mount element (`data-embedded`) and imports its module, which mounts itself. On the site it is the `/quantum-studio/` build; under `wai dev`, Studio's build carries it (`build.mjs --quantum`). - **One line of Quantum Studio changed:** its critique's container (`#qs-critique`) carried `aria-label` with no role, which axe refuses (`aria-prohibited-attr`); it is now `role="group"`. Nothing else of it is touched. - **A viewer needs someone in the room:** the room's clock opens its events track with the first participant, and a viewer that subscribes to it before then is refused `no publisher within RENDEZVOUS_TIMEOUT`. The e2e puts a publisher in the room first (the room page in Chromium with its fake camera and microphone), and Studio shows the room client's own error when a join fails. - **`wai dev --check` checks Studio too** (the page at `/_wai/`, and the stack's state through the API) when its files are found. - **Two build findings:** workspaces that build the `wai` crate with different features must not share a cargo target directory (on the Studio host `wai`'s bin linked another workspace's `libwai` and failed; `e2e/run.sh` gives each workspace its own); and the floor's zstd is C, so on macOS its wasm32 archive needs `llvm-ar` (`AR_wasm32_unknown_unknown`): macOS's `ar` writes an archive with no index the linker can read. ## Checks | Check | Host | Command | Result | |---|---|---|---| | Studio e2e, Chromium + Firefox, both hosts | Studio host (macOS), at `1f1d5f0ad` (the head's code; the head changes one job name) | `bash wai/wai-studio/e2e/run.sh --browsers chromium,firefox` | every check passed (165 PASS: 82 a browser, and the room's publisher) | | Studio e2e, WebKit, both hosts | Framework box: builds on the host, node phase in the browser image of Playwright 1.60.0, at `27a6e7796` (the head) | `STUDIO_PHASES=build bash wai/wai-studio/e2e/run.sh`, then `STUDIO_PHASES=node … --browsers webkit` in the image | every check passed (83 PASS: 82 WebKit, and the room's publisher) | | Unit tests (generation, every drift rule broken in a copy, palette, local core, each panel's core calls on the built wasm) | both of the above | `node --test --test-reporter=tap test/*.test.mjs` with `WAI_STUDIO_CORE_PKG` | `# pass 19`, `# fail 0`, `# skipped 0` | | Drift check | both, and locally | `node scripts/actions.mjs --check` | `Studio's actions hold to the catalogue` | | Core tests | Framework | `cd wai/wai-studio/core && cargo test --locked` | 10 passed | | Core clippy, native and wasm32 | Framework | `cargo clippy --locked --all-targets -- -D warnings`; `--target wasm32-unknown-unknown` | exit 0, both | | Core lock | Framework | `cargo metadata --locked --offline --format-version 1` | current (seeded from `wai-rs/Cargo.lock`: every version is one wai-rs already locks) | | `wai` binary tests, `cli_studio`, `cli_dx`, `floor` | Framework | `cargo test --locked --no-default-features --features quickstart --bin wai --test cli_studio --test cli_dx --test floor` | 25 + 1 + 2 + 3 passed | | clippy on the touched `wai` files | Framework | `cargo clippy --locked --no-default-features --features quickstart --bin wai --test cli_studio`; and `--no-default-features --bin wai` | no note on `dx/dev.rs`, `dx/studio.rs`, `tests/cli_studio.rs` | | `wai-rs` lock | Framework | `cargo metadata --locked --offline` | current, unchanged | | repo checks | local | `check-tests-in-ci.py`, `check-workflow-concurrency.sh`, `check-spec-refs.py`, `check-wai-section-refs.py`, `check-no-vanity-metrics.sh`, `test-public-text-checks.sh`, `check-comparative-prose.sh`, `check-orphan-sources.py`, YAML parse of every workflow | all clean | | public names | local | `check-public-names.sh` on every changed file; commit messages and this body against the same patterns | clean | | after merging `origin/main` | local | `wfsteps.py HEAD <base> HEAD^1 HEAD^2` | 1 problem: the quickstart step of `wai-conformance.yml`, which both sides changed; resolved as the union (`--test cli_fleet --test cli_studio`), diff against main is exactly this PR's lines | ## Falsifications (committed first; each mutation an explicit edit whose anchor matched once, run in an rb checkout) Drift check (Studio host, `node scripts/actions.mjs --check`; unmutated exit 0, restored exit 0): | Mutation | Exit | What it said | |---|---|---| | D1 an action renamed: `envelope.inspect` bound as `envelope.inspekt` | 1 | the action envelope.inspekt has no operation in the catalogue (and its core call and server entry no longer match) | | D2 an operation dropped from the catalogue: `room.join` removed from `ops.pending.json` | 1 | actions.generated.js is not what the catalogue generates; the action room.join has no operation; rooms.js names no operation | | D3 an operation placed for the browser left with no action: `room.leave` unbound | 1 | room.leave is placed client and has no action | | D4 a mismatched name: the core's `envelope_inspect` renamed `envelope_read` | 1 | the core has no call envelope_inspect; envelope_read names envelope.read, which the catalogue does not hold | | D5 the generated table edited by hand (`"menu":"Rooms"`) | 1 | not what the catalogue generates | | D6 `wai dev`'s LOCAL_OPS runs `media.decode` as `media.unpack` | 1 | LOCAL_OPS runs media.unpack, which the catalogue does not hold | `/_wai/` and loopback (Framework, `cargo test --test cli_studio`; unmutated ok, restored ok): | Mutation | Exit | Where it failed | |---|---|---| | M1 `wai dev`'s origin bound to `0.0.0.0` | 101 | the origin is not loopback (cli_studio.rs:144) | | M2 Studio opens a listener of its own on `0.0.0.0` | 101 | "wai dev (the origin)" listens on 0.0.0.0:…, beyond loopback | | M3 `/_wai/` not routed to Studio | 101 | /_wai/ answers with Studio's page | ## Not built, and why - **The release archive does not carry Studio yet** (`share/wai/studio`), so `wai dev` from the quickstart archive serves `/_wai/` only once Studio's files are given (`WAI_STUDIO`). The archive (`wai/quickstart/build.sh`, `build-web.sh`, `wai-release.yml`) is step 7's bundle, which #506 also edits; building the core into it needs Node and wasm-bindgen in the release job. - **The study's other panels:** Projects, Conformance, Keys, Create, and the Cloud views. Not in v1's brief. - **`room.test`, `wai inspect --json` and `--studio DIR`** wait on #506 (see Decisions). - **The local core runs inspect, decode and encode;** verify, wrap and unwrap run in the tab (the same library), so the local core adds nothing for them yet. - **WebKit on the Studio host:** it never launches from an SSH session there (nor from the SSH-started macOS runner), so it runs on Linux in Playwright's image, here on the Framework box and in CI's `webkit · linux` job. - **Not deployed:** the site branch `studio-v1` is pushed for review (WAI site repository); its `/studio/` serves this PR's head and must be re-synced at the merge commit before a deploy. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wai/wai-studio: the panels (Inspect, Verify and receipts, Encode/decode/package, Rooms,
Run, Quantum) and the command palette; actions generated from the operation catalogue
with a drift check; the core (wai-studio-core) binding the wai crate for the tab. wai dev
serves Studio at /_wai/ on its own origin, with the dev stack's routes for it under
/_wai/api/.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A viewer follows the room's clock, whose events track opens with the room's first participant.
The macOS runner and the Studio host run WebKit from SSH, where it never launches, so the CI
runs WebKit in Playwright's image on the linux runner.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge remote-tracking branch 'origin/main' into wai/studio-web
Some checks failed
wai-fleet service / does this change touch it (pull_request) Has been cancelled
wai-fleet service / service · linux (pull_request) Has been cancelled
wai-fleet service / service · macos (pull_request) Has been cancelled
wai-ledger service / does this change touch it (pull_request) Has been cancelled
wai-ledger service / service · linux (pull_request) Has been cancelled
wai-ledger service / service · macos (pull_request) Has been cancelled
wai-pack / wai-pack · does this change touch it (pull_request) Has been cancelled
wai-pack / wai-pack · linux (pull_request) Has been cancelled
wai-pack / wai-pack · linux-arm64 (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been cancelled
wai-pack / wai-pack · dependency licences (pull_request) Has been cancelled
wai-quantum studio and desktop / does this change touch it (pull_request) Has been cancelled
wai-quantum studio and desktop / studio · browser (pull_request) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (pull_request) Has been cancelled
wai-sink-android native sink library / does this change touch it (pull_request) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Has been cancelled
wai-sink-swift native sink package / does this change touch it (pull_request) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Has been cancelled
WAI Studio / does this change touch it (pull_request) Has been cancelled
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Has been cancelled
WAI Studio / Studio in Chromium, WebKit and Firefox · macos (pull_request) Has been cancelled
WAI Studio / Studio in WebKit · linux (pull_request) Has been cancelled
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been cancelled
wai-web wasm32 / does this change touch it (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
7e8f7370d4
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-studio-core: wasm-pack builds it without wasm-opt, as wai-web
Some checks failed
wai-fleet service / does this change touch it (pull_request) Has been cancelled
wai-fleet service / service · linux (pull_request) Has been cancelled
wai-fleet service / service · macos (pull_request) Has been cancelled
wai-ledger service / does this change touch it (pull_request) Has been cancelled
wai-ledger service / service · linux (pull_request) Has been cancelled
wai-ledger service / service · macos (pull_request) Has been cancelled
wai-pack / wai-pack · does this change touch it (pull_request) Has been cancelled
wai-pack / wai-pack · linux (pull_request) Has been cancelled
wai-pack / wai-pack · linux-arm64 (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been cancelled
wai-pack / wai-pack · dependency licences (pull_request) Has been cancelled
wai-quantum studio and desktop / does this change touch it (pull_request) Has been cancelled
wai-quantum studio and desktop / studio · browser (pull_request) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (pull_request) Has been cancelled
wai-sink-android native sink library / does this change touch it (pull_request) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Has been cancelled
wai-sink-swift native sink package / does this change touch it (pull_request) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Has been cancelled
WAI Studio / does this change touch it (pull_request) Has been cancelled
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Has been cancelled
WAI Studio / Studio in Chromium, WebKit and Firefox · macos (pull_request) Has been cancelled
WAI Studio / Studio in WebKit · linux (pull_request) Has been cancelled
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been cancelled
wai-web wasm32 / does this change touch it (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
a745de9ba3
Co-Authored-By: Claude Opus 5.5 <[email protected]>
WAI Studio: Escape closes the palette from its search field too; the e2e holds it
Some checks failed
wai-fleet service / does this change touch it (pull_request) Has been cancelled
wai-fleet service / service · linux (pull_request) Has been cancelled
wai-fleet service / service · macos (pull_request) Has been cancelled
wai-ledger service / does this change touch it (pull_request) Has been cancelled
wai-ledger service / service · linux (pull_request) Has been cancelled
wai-ledger service / service · macos (pull_request) Has been cancelled
wai-pack / wai-pack · does this change touch it (pull_request) Has been cancelled
wai-pack / wai-pack · linux (pull_request) Has been cancelled
wai-pack / wai-pack · linux-arm64 (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been cancelled
wai-pack / wai-pack · dependency licences (pull_request) Has been cancelled
wai-quantum studio and desktop / does this change touch it (pull_request) Has been cancelled
wai-quantum studio and desktop / studio · browser (pull_request) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (pull_request) Has been cancelled
wai-sink-android native sink library / does this change touch it (pull_request) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Has been cancelled
wai-sink-swift native sink package / does this change touch it (pull_request) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Has been cancelled
WAI Studio / does this change touch it (pull_request) Has been cancelled
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Has been cancelled
WAI Studio / Studio in Chromium, WebKit and Firefox · macos (pull_request) Has been cancelled
WAI Studio / Studio in WebKit · linux (pull_request) Has been cancelled
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been cancelled
wai-web wasm32 / does this change touch it (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
1f1d5f0ad4
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-studio.yml: the macOS job named for the browsers it runs
Some checks failed
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 35s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 1m55s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 18m58s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 13m9s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 5m20s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 9m10s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m46s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 24s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (pull_request) Successful in 1m0s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 2m21s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 24m33s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 2m42s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 8m2s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 7m2s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 16m51s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 1m43s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 11m16s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 51s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m46s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 6m29s
wai-fleet service / service · macos (pull_request) Successful in 41s
wai-ledger service / service · macos (pull_request) Successful in 51s
wai-pack / wai-pack · macos (pull_request) Successful in 3m21s
WAI conformance / cargo-class corpora · macos (pull_request) Failing after 17m11s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h5m40s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 2m49s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 15m23s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 7m49s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 9m55s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 16m33s
27a6e77967
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The front answers a refusal (another origin, 403) before reading the request's
body. Closing a socket with bytes still unread makes the system reset the
connection, and on macOS the client then lost the 403 it had been sent:
studios_api_answers_this_origin_once_the_stack_is_up failed on the macOS CI
runner with "Connection reset by peer". Each connection is now half-closed
after its answer and drained for at most a second before it closes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge remote-tracking branch 'origin/main' into wai/studio-web
All checks were successful
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 10m7s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m54s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 6m7s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 40s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 17m53s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m53s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 10m51s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m22s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 22m13s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 2m40s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 6m59s
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (pull_request) Successful in 35s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (pull_request) Successful in 59s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 7m29s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 14m58s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 58m17s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 6m57s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 46s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 9m41s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m1s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 4m48s
wai-fleet service / service · macos (pull_request) Successful in 43s
wai-ledger service / service · macos (pull_request) Successful in 48s
wai-pack / wai-pack · macos (pull_request) Successful in 2m48s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 17m17s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 3m29s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 12m47s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 7m8s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 9m27s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 16m13s
27116e23bf
Merge remote-tracking branch 'origin/main' into wai/studio-web
All checks were successful
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 1m35s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m8s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 9m50s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 27s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m6s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 5m41s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 11m27s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 2m5s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 44s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 10m38s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m20s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 7m31s
wai-fleet service / service · macos (pull_request) Successful in 41s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 2m32s
wai-ledger service / service · macos (pull_request) Successful in 57s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (pull_request) Successful in 1m45s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 7m41s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 22m33s
wai-pack / wai-pack · macos (pull_request) Successful in 3m14s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 4m15s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 3m47s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 21m14s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 14m58s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 7m40s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 10m8s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 16m41s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (pull_request) Successful in 1m4s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 6m12s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 15m0s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 59m29s
fa63d71a1c
# Conflicts:
#	wai/CHANGELOG.md
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!522
No description provided.