feat(wit): rename the WIT namespace falcon:* → pulseengine:falcon-* (OCI-P04) - #337
Merged
Conversation
…(OCI-P04, v1.133)
wasm.directory indexes COMPONENTS by registry namespace but INTERFACES by WIT
package namespace, and ours disagreed: components registered under
`pulseengine`, WIT packages named `falcon:*`. So a component page linked its
exports into /falcon/... — an ORPHANED namespace reporting "0 packages", no
owner, no registry. Reported from the live listing.
Renaming to a single namespace instead of claiming a second one:
falcon:cascade -> pulseengine:falcon-cascade
falcon:cascade-stream -> pulseengine:falcon-cascade-stream
falcon:cascade-step -> pulseengine:falcon-cascade-step
falcon:control -> pulseengine:falcon-control
falcon:control-rate -> pulseengine:falcon-control-rate
falcon:flight -> pulseengine:falcon-flight
CORRECTING MY OWN ASSESSMENT: I had called this "far too much breakage" and
recommended publishing a separate WIT package to claim `falcon` instead. I had
not measured it. The real blast radius is 37 references — 7 WIT files, 15 Rust
sources, 8 Cargo.toml, 1 BUILD.bazel — all inside relay, with the interfaces
still at 0.7.0 (pre-1.0, so a rename is semantically fair). The simpler option
was the right one.
It also buys a property worth having: the WIT package name now MATCHES the OCI
package name — `pulseengine:falcon-cascade` <-> ghcr.io/pulseengine/falcon-cascade
— so the component page and its interface pages resolve into the same owned
namespace.
Note the generated Rust module paths do NOT contain ':' and were missed by the
first pass: `pulseengine:falcon-cascade` generates `pulseengine::falcon_cascade`,
so `exports::falcon::cascade::` had to be rewritten separately (22 .rs files),
as did the wasmtime host accessor (`.falcon_cascade_rate()` ->
`.pulseengine_falcon_cascade_rate()`).
VERIFIED, not assumed:
- rate rebuilds and now declares
import pulseengine:falcon-cascade/types@0.7.0
export pulseengine:falcon-cascade/rate@0.7.0
- still a component (0061736d0d000100), 0 wasi imports, 0 memory.grow, 4026 B
- the through-wasm closed-loop proof is unchanged: converges 0.193 s,
|err| 0.0059 rad/s
- attitude and falcon-mixer rebuild clean; zero stale `falcon:` / `falcon::`
references remain
- rivet validate PASS
BREAKING for consumers: components published up to falcon-v1.131.1 export
`falcon:cascade/...`; from the next release they export
`pulseengine:falcon-cascade/...`. jess is mid-reshape on this seam (jess#167),
so the change rides along with one they are already making — flagged there.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG
avrabe
enabled auto-merge (squash)
August 6, 2026 17:51
This was referenced Aug 6, 2026
Per the two-commit rule an implementation PR sets at most `implemented`; the promotion to `verified` is a separate code-free verify PR after the release exercises it. Precedent: OCI-P01 landed at `implemented` in 4d454f2 and was promoted in bb5b810. Caught before tagging: all three v1.133 artifacts were still `proposed` on their own implementation branches, which would have put three unimplemented- looking artifacts in the release scope at tag time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG
The push that would have triggered CI landed during the outage (Actions was in major_outage 15:22Z-00:0xZ; zero runs were created repo-wide for hours), so no run exists for this branch. Empty commit to generate a fresh synchronize event. Squash-merge drops it from main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG
avrabe
added a commit
that referenced
this pull request
Aug 7, 2026
#338) * feat(oci): publish under the nested path ghcr.io/<owner>/falcon/<name> Mirrors how wasi publishes — ghcr.io/webassembly/wasi/io under namespace `wasi`, with the registry entry keeping a flat display name and pointing `repository` at the nested path (`name = "io"`, `repository = "wasi/io"`). Everything falcon now groups under one path instead of a flat hyphenated list: ghcr.io/pulseengine/falcon-rate -> ghcr.io/pulseengine/falcon/rate ghcr.io/pulseengine/falcon-iekf -> ghcr.io/pulseengine/falcon/iekf ... and the WIT package at ghcr.io/pulseengine/falcon/cascade Bundled DELIBERATELY with the falcon:* -> pulseengine:falcon-* WIT rename (#337) so consumers absorb ONE break rather than two. Paths published up to falcon-v1.131.1 keep serving their existing versions; nothing is retroactive. CONSEQUENCE THAT MUST NOT BE MISSED — the registered wasm.directory entries point at the FLAT repositories (`repository = "falcon-rate"`). Once releases publish to the nested paths those entries are stale: the listings keep resolving to repositories that no longer receive versions. And a `registry-entry` issue only ADDS an entry — changing an existing one needs a PR against registry/pulseengine.toml. Recorded in docs/OCI-DISTRIBUTION.md with the target shape, to be done AFTER the first nested-path release so the entries are never pointing at nothing. Stacks on #337 (the namespace rename); the matching OCI-P04 verification criteria update lands there once it merges. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG * ci: re-dispatch after the 2026-08-06 GitHub Actions outage The push that would have triggered CI landed during the outage (Actions was in major_outage 15:22Z-00:0xZ; zero runs were created repo-wide for hours), so no run exists for this branch. Empty commit to generate a fresh synchronize event. Squash-merge drops it from main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
avrabe
added a commit
that referenced
this pull request
Aug 7, 2026
…OCI-P02, v1.133)
All eight Component-Model components now build `no_std` for
wasm32-unknown-unknown: ZERO `wasi:*` imports and ZERO `memory.grow` each.
`memory.grow` is what makes `meld fuse --memory shared --address-rebase` reject
a component (gale#89, meld#299), so this is what lets the fused cascade lower to
a single-address-space MCU image. Previously only `rate` was converted, and the
others' WASI imports were publicly visible on their wasm.directory listings
(falcon-iekf and falcon-attitude carried 14 each).
Measured, per published component:
flight 106,834 -> 34,988 B iekf 60,894 -> 11,675 B
position 57,071 -> 7,525 B attitude 53,570 -> 4,086 B
mixer 16,103 -> 2,964 B rate 3,908 -> 4,026 B
(+ ekf 4,801 B and cascade 6,321 B, bundled but not published)
SHARED RUNTIME rather than six copies. The conversion needs the same three
pieces each time — a bounded work-memory arena, the canonical-ABI
`cabi_realloc` export (wit-bindgen-rt supplies it only when std is linked), and
a panic handler — which is ~60 lines of `unsafe` per component. Factored into
crates/falcon-cm-rt with an `export_cm_rt!()` macro, because
`#[global_allocator]`/`#[no_mangle]`/`#[panic_handler]` must be defined in the
final artifact while the logic can live in one audited place. `rate` was
retrofitted onto it: 214 -> 95 lines, byte-identical behaviour (4,026 B,
0 grow, 0 wasi, closed-loop proof unchanged at 0.193 s / |err| 0.0059 rad/s).
Two things `thread_local!` was hiding, now explicit:
- it is `std`, and was the sole reason these components imported WASI at all;
- it allows LAZY initialisation, while a plain `static` needs a const one.
`Iekf::level()` is not const (it builds a NavState), so iekf now
lazily initialises through an `Option`. Making the constructor const would
have meant editing relay-iekf — a Kani-verified flight crate — for the sake
of a static; not worth it.
Bazel targets opt into `std` EXPLICITLY (6 of them): rules_rust cannot build the
no_std variant yet — it lacks the crate_universe dep and a genuine no_std
config, and would otherwise fail on a duplicate `panic_impl` lang item. The two
build paths therefore still differ, as documented; converging them stays the
open follow-up.
Verified per component: header 0061736d0d000100, `wasm-tools`-measured 0 wasi
imports and 0 memory.grow, and the through-wasm closed-loop proof unchanged.
rivet validate PASS.
Stacks on #337 (the pulseengine:falcon-* rename).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG
avrabe
added a commit
that referenced
this pull request
Aug 7, 2026
…OCI-P02) (#339) * feat(oci): convert the whole cascade to no_std — WASI-free, no-grow (OCI-P02, v1.133) All eight Component-Model components now build `no_std` for wasm32-unknown-unknown: ZERO `wasi:*` imports and ZERO `memory.grow` each. `memory.grow` is what makes `meld fuse --memory shared --address-rebase` reject a component (gale#89, meld#299), so this is what lets the fused cascade lower to a single-address-space MCU image. Previously only `rate` was converted, and the others' WASI imports were publicly visible on their wasm.directory listings (falcon-iekf and falcon-attitude carried 14 each). Measured, per published component: flight 106,834 -> 34,988 B iekf 60,894 -> 11,675 B position 57,071 -> 7,525 B attitude 53,570 -> 4,086 B mixer 16,103 -> 2,964 B rate 3,908 -> 4,026 B (+ ekf 4,801 B and cascade 6,321 B, bundled but not published) SHARED RUNTIME rather than six copies. The conversion needs the same three pieces each time — a bounded work-memory arena, the canonical-ABI `cabi_realloc` export (wit-bindgen-rt supplies it only when std is linked), and a panic handler — which is ~60 lines of `unsafe` per component. Factored into crates/falcon-cm-rt with an `export_cm_rt!()` macro, because `#[global_allocator]`/`#[no_mangle]`/`#[panic_handler]` must be defined in the final artifact while the logic can live in one audited place. `rate` was retrofitted onto it: 214 -> 95 lines, byte-identical behaviour (4,026 B, 0 grow, 0 wasi, closed-loop proof unchanged at 0.193 s / |err| 0.0059 rad/s). Two things `thread_local!` was hiding, now explicit: - it is `std`, and was the sole reason these components imported WASI at all; - it allows LAZY initialisation, while a plain `static` needs a const one. `Iekf::level()` is not const (it builds a NavState), so iekf now lazily initialises through an `Option`. Making the constructor const would have meant editing relay-iekf — a Kani-verified flight crate — for the sake of a static; not worth it. Bazel targets opt into `std` EXPLICITLY (6 of them): rules_rust cannot build the no_std variant yet — it lacks the crate_universe dep and a genuine no_std config, and would otherwise fail on a duplicate `panic_impl` lang item. The two build paths therefore still differ, as documented; converging them stays the open follow-up. Verified per component: header 0061736d0d000100, `wasm-tools`-measured 0 wasi imports and 0 memory.grow, and the through-wasm closed-loop proof unchanged. rivet validate PASS. Stacks on #337 (the pulseengine:falcon-* rename). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG * chore(rivet): OCI-P02 — the no_std cascade conversion is implemented Per the two-commit rule an implementation PR sets at most `implemented`; the promotion to `verified` is a separate code-free verify PR after the release exercises it. Precedent: OCI-P01 landed at `implemented` in 4d454f2 and was promoted in bb5b810. Caught before tagging: all three v1.133 artifacts were still `proposed` on their own implementation branches, which would have put three unimplemented- looking artifacts in the release scope at tag time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG * ci: re-dispatch after the 2026-08-06 GitHub Actions outage The push that would have triggered CI landed during the outage (Actions was in major_outage 15:22Z-00:0xZ; zero runs were created repo-wide for hours), so no run exists for this branch. Empty commit to generate a fresh synchronize event. Squash-merge drops it from main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
avrabe
added a commit
that referenced
this pull request
Aug 7, 2026
…erifiers (#346) All three v1.133 requirements sat at `implemented` with NO verification artifact pointing at them. The release-execution traceability gate blocks a tag in that state, and it should: `implemented` with no verifier is a claim with no evidence. This was my own gap. I bumped the three to `implemented` citing OCI-P01 as precedent — but OCI-P01 landed at `implemented` WITH a verifier (FV-FALCON-OCI-001) attached. I replicated the status and not the evidence. Adds one verifier each, with CI-runnable assertions rather than prose: FV-FALCON-REL-001 -> SWREQ-FALCON-REL-P01 both `gh release create` paths pass --draft; one terminal publish step; the malformed-tag guard exists. FV-FALCON-OCI-002 -> SWREQ-FALCON-OCI-P02 8/8 components are no_std AND export the shared bounded-arena runtime. Asserted as EIGHT, not "at least one": v1.129 shipped a partially converted cascade, and one std stage re-introduces WASI and memory.grow into the fused image. FV-FALCON-OCI-003 -> SWREQ-FALCON-OCI-P04 the package declares pulseengine:falcon-cascade AND there are ZERO remaining `falcon:cascade` references. The negative assertion carries the weight — a PARTIAL rename is worse than none. These are real verifiers, not decoration — each was observed to FAIL before its implementation merged and PASS after: OCI-003 PASS once #337 landed OCI-002 1/8 -> 8/8 the moment #339 landed REL-001 0/2 -> 2/2 the moment #340 landed A step that cannot fail is not evidence; these can, and did. Also caught by running the gate rather than trusting the draft: an `gh release view falcon-v1.133.0 ... # bench-only` step really EXECUTED and failed rc=1. The gate detects bench-only by COMMAND SHAPE (BENCH_PATTERNS) — rivet strips shell comments at the YAML->JSON boundary, so the marker is inert. Removed, with the reasoning recorded in the artifact: a step whose result depends on WHEN it runs is not evidence. All three stay `implemented`, not `verified`. The binding evidence — published blobs, and an actual draft->published transition — only exists once falcon-v1.133.0 has run. Promotion is the post-tag verify PR, per the two-commit rule. Verified locally: gate scoped to these artifacts: 7/7 steps PASS, exit 0, 5 bench-only skipped rivet validate (v0.19.0 — the version the GATE pins, not local 0.28): PASS Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the namespace split found on the live listings (#336).
The problem
wasm.directory indexes components by registry namespace but interfaces by WIT package namespace. Ours disagreed — components under
pulseengine, WIT packages namedfalcon:*— sopulseengine/falcon-iekflinked its exports into/falcon/cascade/..., an orphaned namespace reporting "0 packages", no owner, no registry.The fix — one namespace, not two
Correcting my own assessment: I'd called this "far too much breakage" and proposed publishing a separate WIT package to claim
falconinstead — without measuring it. Actual blast radius: 37 references, all inside relay, interfaces still at 0.7.0 (pre-1.0). The simpler option was the right one.It also buys a real property: the WIT package name now matches the OCI package name —
pulseengine:falcon-cascade↔ghcr.io/pulseengine/falcon-cascade— so component and interface pages resolve into the same owned namespace.A subtlety worth noting
Generated Rust module paths contain no
:, so the first pass missed them —pulseengine:falcon-cascadegeneratespulseengine::falcon_cascade. Required a separate rewrite of 22.rsfiles plus the wasmtime host accessor (.falcon_cascade_rate()→.pulseengine_falcon_cascade_rate()).Verified, not assumed
raterebuilds and declaresimport/export pulseengine:falcon-cascade/{types,rate}@0.7.00061736d0d000100), 0 wasi imports, 0 memory.grow, 4026 Battitude+falcon-mixerrebuild clean; zero stalefalcon:/falcon::refsrivet validatePASSBreaking for consumers
Components up to v1.131.1 export
falcon:cascade/...; from the next release,pulseengine:falcon-cascade/.... jess is mid-reshape on this seam (jess#167), so it rides along with a change they're already making — flagging there.Refs: #336,
SWREQ-FALCON-OCI-P04.🤖 Generated with Claude Code