Skip to content

feat(wit): rename the WIT namespace falcon:* → pulseengine:falcon-* (OCI-P04) - #337

Merged
avrabe merged 4 commits into
mainfrom
feat/oci-p04-wit-package-namespace
Aug 7, 2026
Merged

feat(wit): rename the WIT namespace falcon:* → pulseengine:falcon-* (OCI-P04)#337
avrabe merged 4 commits into
mainfrom
feat/oci-p04-wit-package-namespace

Conversation

@avrabe

@avrabe avrabe commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

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 named falcon:* — so pulseengine/falcon-iekf linked its exports into /falcon/cascade/..., an orphaned namespace reporting "0 packages", no owner, no registry.

The fix — one namespace, not two

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'd called this "far too much breakage" and proposed publishing a separate WIT package to claim falcon instead — 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 namepulseengine:falcon-cascadeghcr.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-cascade generates pulseengine::falcon_cascade. Required a separate rewrite of 22 .rs files plus the wasmtime host accessor (.falcon_cascade_rate().pulseengine_falcon_cascade_rate()).

Verified, not assumed

  • rate rebuilds and declares import/export pulseengine:falcon-cascade/{types,rate}@0.7.0
  • still a component (0061736d0d000100), 0 wasi imports, 0 memory.grow, 4026 B
  • closed-loop proof unchanged — converges 0.193 s, |err| 0.0059 rad/s
  • attitude + falcon-mixer rebuild clean; zero stale falcon:/falcon:: refs
  • rivet validate PASS

Breaking 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

…(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 and others added 2 commits August 6, 2026 21:23
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
avrabe merged commit 41fb72f into main Aug 7, 2026
14 checks passed
@avrabe
avrabe deleted the feat/oci-p04-wit-package-namespace branch August 7, 2026 02:45
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>
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