Skip to content

Cut Workshop's build dependence on the Gateway - #187

Merged
vinniefalco merged 4 commits into
cppalliance:masterfrom
vinniefalco:master
Oct 31, 2026
Merged

vinniefalco merged 4 commits into
cppalliance:masterfrom
vinniefalco:master

Conversation

@vinniefalco

@vinniefalco vinniefalco commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

Workshop and the Harness should reach the Gateway only through a written API. That means no dependency on a Gateway crate, no test that reads a file the Gateway owns, and no Workshop build step that compiles the Gateway. These commits remove those three couplings. Workshop gets its own copy of the discovery and progress types, its own copies of the fixtures its tests read, and a sidecar that comes from a pinned Gateway release or a stub instead of a source build of the Gateway in this tree.

Give Workshop its own Gateway discovery and progress type

crates/workshop/gateway-discovery now holds the discovery file reader, the liveness and health probes, and the validated discovery document, all previously reached through gateway-api-discovery. The same_origin_authority_allowed predicate moved from shared-loopback into crates/workshop/server/src/routes/realtime-origin.rs.

The inline copy only narrows visibility, from pub to pub(super); the body and behavior are unchanged.

Give Workshop its own copies of the shared test fixtures

crates/workshop/server/tests/fixtures/realtime holds the five Realtime protocol examples, and the Workshop relay test and UI suite read them there rather than from crates/gateway/stt/. crates/workshop/ui/test/fixtures/replay holds the eleven snapshot streams plus ui-baseline.json and ui-metrics.json, which stt-replay.mjs writes through writeJson. The Gateway's baseline.json and metrics.json drop their ui section, and replay.rs removes ui from FOREIGN_SECTIONS.

A copy drifts, so crates/build-xtask/tests/realtime_wire_conformance.rs compares the two Realtime fixture directories byte for byte and names the file and the first difference. It lives in build-xtask because that is the only crate allowed to read both families; a Workshop crate may not depend on a Gateway crate. docs-claims.mjs also loses its exemption for crates/gateway/stt/, so the wording scan now applies one rule to every file.

Stage the Gateway sidecar from a pinned release

gateway-pin/pin.json records the release repository, the tag and version, the min_gateway floor, and the checksum and size of each asset. tools/fetch-gateway-sidecar.mjs downloads one target, verifies its checksum and optionally its attestation, and stages it where Tauri's externalBin resolves; --stub writes a compile-only file instead.

The staged file gets a .pinned marker beside it. crates/workshop/desktop/build.rs sees that marker through sidecar_is_pinned and leaves the file alone instead of refreshing it from target/<profile>/runvil-gateway. In crates/build-workshop/src/main/pipeline.rs, gateway_source returns GatewaySource::Staged { stub } or GatewaySource::Given, and falls back to GatewaySource::Build only when nothing else supplies a binary. cargo workshop accepts --gateway-binary and RUNVIL_GATEWAY_SIDECAR for the given case.

The version check ties the running Gateway to the pin. crates/workshop/gateway/build.rs reads min_gateway and sets RUNVIL_MIN_GATEWAY, which crates/workshop/gateway/src/version.rs exposes as MIN_GATEWAY. VersionCheck::compare reads an empty minimum as NoMinimumRecorded, so a placeholder pin switches the check off. crates/workshop/server/src/app/compose.rs calls report_version with the version from the discovery identity when one exists; a config with no discovery file has nothing to compare and skips the check.

Warn instead of passing when the Gateway pin is unfilled

An unfilled pin used to produce no signal at all. announce now logs NoMinimumRecorded at tracing::warn! and pushes a status line in a release build. The new Build type separates a developer's debug build, which reaches this state on every launch while the pin is a placeholder, from a release build, which is what an installer ships. Build::current decides at compile time from cfg!(debug_assertions), so the build script emits no second value. The status line goes through push_status_update, the same call the older-Gateway warning uses, so it reaches the first session through the retained status bus. The Satisfied arm is unchanged and still pushes nothing, so a filled pin that passes stays quiet.

Stop linking a public doc to a private function

The rustdoc on announce in crates/workshop/gateway/src/version.rs named announce_for, which is private. cargo doc with RUSTDOCFLAGS=-D warnings rejected workshop-gateway, and the docs job failed on the first push of this branch. The sentence now names announce_for as plain code instead of a link. The same text broke twice, once as announce and once as its re-export announce_version.

What this does not do

No Gateway release exists yet, so gateway-pin/pin.json and both committed spec copies still hold PLACEHOLDER. nightly.yml and release-workshop.yml will fail at the fetch step until someone runs node tools/bump-gateway.mjs <tag> against a published release.

Nothing here prevents a release being built from an unfilled pin. The version check warns loudly in a shipped build but does not block it. The --stub path exists for compile-only jobs and prints a warning; a build made with it cannot run a Gateway.

How this was verified

Run locally at 4cf1430ad:

  • cargo test -p workshop-gateway 102 passed, 1 ignored
  • cargo test -p workshop-server 145 passed in the unit tests and 76 in the integration tests, 1 ignored in each
  • cargo test -p build-workshop 25 passed and 1 passed
  • cargo test -p workshop-gateway-discovery 75 passed
  • cargo test -p gateway-stt 307 passed and 87 passed
  • cargo test -p build-xtask 264, 7, and 3 passed
  • node --test tools/*.test.mjs 65 passed, 0 failed
  • cargo fmt --all -- --check clean
  • cargo clippy --workspace --all-targets --locked clean

The pre-push hook passed on both pushes, which runs the wording scan, a headless Gateway check, workspace Clippy, and cargo deny check.

On CI, every job passes: docs, clippy, fmt, msrv, api-surface, supply-chain, test, ui, check-workshop, check-workshop-linux, compile-nsis, pure-stt-state, and plan. check-workshop and check-workshop-linux both take the stub sidecar path.

Reviewer notes

  • The inlined same_origin_authority_allowed in realtime-origin.rs is byte-identical in behavior to the workshop_same_origin_authority_allowed function it replaced in shared-loopback. Only the visibility narrowed, from pub to pub(super).
  • The vendored Realtime fixtures are a copy with a byte-for-byte conformance check in crates/build-xtask/tests/realtime_wire_conformance.rs. The replay streams are a frozen snapshot with no drift check, so a Gateway edit to a replay fixture will not fail the Workshop suite.
  • The discovery fork duplicates 2,576 non-test lines across crates/workshop/gateway-discovery/src, so a platform fix has to be made in two places.
  • The Windows agent tests are load-sensitive because they share one 10 second deadline, declared as DEADLINE in crates/workshop/agents/tests/it/conversation.rs.

`crates/workshop/server/tests/fixtures/realtime` now holds the five Realtime protocol examples, and the Workshop relay and UI tests read them from there instead of `crates/gateway/stt/`. `crates/workshop/ui/test/fixtures/replay` holds the 11 snapshot streams plus `ui-baseline.json` and `ui-metrics.json`, which `stt-replay.mjs` writes through `writeJson`. The Gateway's `baseline.json` and `metrics.json` lose their `ui` section, and `replay.rs` drops `ui` from `FOREIGN_SECTIONS`.

- The drift check sits in `crates/build-xtask/tests/realtime_wire_conformance.rs`, because a Workshop crate may not depend on a Gateway crate. It compares the two Realtime fixture directories byte for byte and names the file and the first difference.
- `docs-claims.mjs` drops the exemption for `crates/gateway/stt/`, so the `engine` and `harness` scan now applies one rule to every file.
- The snapshot streams and the `ui` baselines are a frozen copy with no drift check. A Gateway edit to a replay fixture does not fail the Workshop suite.
Workshop obtains its Gateway sidecar from a pinned release or a stub, and builds the Gateway from source only when nothing else supplies one. `gateway-pin/pin.json` records the release, the checksum and size of each asset, and the `min_gateway` floor. `tools/fetch-gateway-sidecar.mjs` downloads, verifies, and stages one target, `--stub` writes a compile-only file, and `cargo workshop` accepts `--gateway-binary` and `RUNVIL_GATEWAY_SIDECAR`.

- A sidecar staged by `tools/fetch-gateway-sidecar.mjs` leaves a `.pinned` marker beside it, and `crates/workshop/desktop/build.rs` then leaves the file alone instead of refreshing it from `target/<profile>/runvil-gateway`.
- A placeholder pin yields an empty `RUNVIL_MIN_GATEWAY`, which `VersionCheck::compare` reads as `NoMinimumRecorded`. `announce` logs that case at `tracing::info!` and pushes no status line, so an unfilled pin turns the version check off with no visible sign.
- `gateway-pin/pin.json` and both committed spec copies still hold `PLACEHOLDER`, so `nightly.yml` and `release-workshop.yml` fail at the fetch until someone runs `node tools/bump-gateway.mjs <tag>` against a published Gateway release.
`announce` now logs the `NoMinimumRecorded` case at `tracing::warn!` and pushes a status line in a release build. The new `Build` type separates a debug build, which reaches this state on every launch while the pin is a placeholder, from a release build, which is what an installer ships.

- The split is decided at compile time by `Build::current` from `cfg!(debug_assertions)`, so the build script emits no second value and needs no change.
- The status line goes through `push_status_update`, the same call the older-Gateway warning uses, so it reaches the first session through the retained status bus.
- The `Satisfied` arm is unchanged and still pushes nothing, so a filled pin that passes the check stays quiet.
The rustdoc on announce named announce_for, which is private, so cargo doc with -D warnings rejected workshop-gateway and the docs job failed. The sentence now names announce_for as plain code instead of a link.

- The same text broke twice, as announce and as its re-export announce_version.

- cargo doc --workspace --no-deps --all-features, the runvil and harness facade builds, and the runvil-engine private-items build all exit 0 with RUSTDOCFLAGS=-D warnings.
@vinniefalco
vinniefalco merged commit 2643dbe into cppalliance:master Oct 31, 2026
20 checks passed

This branch was successfully deployed

1 active deployment
github-pages — 2643dbec Deployed Oct 31, 2026 by vinniefalco via deploy #151
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