Skip to content

recover(launcher): 14a.5b libgossamer bridges (re-targets PR #29 at main)#35

Merged
hyperpolymath merged 1 commit into
mainfrom
feat/recover-14a-5b-bridges
May 20, 2026
Merged

recover(launcher): 14a.5b libgossamer bridges (re-targets PR #29 at main)#35
hyperpolymath merged 1 commit into
mainfrom
feat/recover-14a-5b-bridges

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Why this PR exists

PR #29 ("feat(cli/launcher): libgossamer bridges + eager-grant caps (14a.5b)") shows as MERGED on GitHub but its commits never landed on main. The PR was stacked on the 14a.5a feature branch (`feat/cli-launcher-mvp-14a5a`); when the GitHub UI's "Merge pull request" button was clicked, the merge commit landed on that feature branch — which was then deleted, orphaning the work.

Same trap affected PRs #30, #33, #34. Recovery sequence is four separate PRs each based on main; this is PR 1 of 4.

What this PR lands

File Change
`cli/launcher/src/bridges.zig` (new, ~470 LOC) 29 `env::gossamer_*` host imports + `env::cap_token` + the marshalling helpers + the Imports table + `grantCaps`
`cli/launcher/src/main.zig` Makes `HostEnv` / `ImportSpec` / `guestSlice` / `c` `pub`; adds `cap_tokens: [6]u64` field; registers `bridges.Imports` + calls `grantCaps`
`cli/launcher/build.zig` Links libgossamer dynamically (`addLibraryPath` + `linkSystemLibrary("gossamer")`)
`cli/launcher/src/hello-bridges.wat` (new) Smoke wat — calls `gossamer_version_to` + `print_string`
`cli/launcher/README.adoc` Documents the 29 bridges + new smoke test

Bridge coverage

Family Symbols
String-out `gossamer_version_to`, `gossamer_build_info_to`, `gossamer_last_error_to`
Webview lifecycle `gossamer_create_ex`, `gossamer_navigate`, `gossamer_load_html`, `gossamer_channel_open`, `gossamer_set_csp`, `gossamer_set_title`, `gossamer_registry_add`, `gossamer_run`
Groove `gossamer_groove_discover`, `gossamer_groove_status`
Watcher `gossamer_watcher_start`, `gossamer_watcher_stop`
Shell `gossamer_shell_spawn`, `gossamer_shell_kill`
Filesystem `gossamer_fs_read_text`, `gossamer_fs_write_text`, `gossamer_fs_exists`, `gossamer_fs_mkdir_p`, `gossamer_fs_copy_file`
Conf `gossamer_conf_load` + `get_string` / `get_int` / `get_bool` / `has` / `free`
Capability tokens `cap_token`

ABI conventions

  • Inputs: strings cross as `(ptr: i32, len: i32)` pairs. Bridge dupes into host-side null-terminated buffer, calls libgossamer, frees.
  • Outputs: strings use WASI-style write-into-caller-buffer (`buf_ptr, buf_len`) → bytes written or -1.
  • Opaque handles (watcher, shell child, conf): cross as `i64` (bit-cast u64 = host pointer).
  • Capability tokens: eager-granted at launcher startup for ResourceKind 0/1/2 (FileSystem/Network/Shell). Guest reads via `env::cap_token(kind)` rather than calling `gossamer_cap_grant` itself.

NOT in this PR (intentionally — will land in PRs 2/3/4 of 4)

  • Build integration / cli.wasm compile (PR 2)
  • Main.eph subcommand dispatcher (PR 3)
  • Ephapax String FFI bridges + argv[0]-name dispatch (PR 4)

Verification

  • `git rebase origin/main` — clean, only the 14a.5b delta commit applied.
  • `zig ast-check` on `main.zig`, `bridges.zig`, `build.zig` — clean.
  • Identical content to original PR feat(cli/launcher): libgossamer bridges + eager-grant caps (14a.5b) #29 except for the rebase target.
  • Reviewer: `zig build` succeeds with libwasmtime + libgossamer installed. (Local WSL env doesn't have GTK3 dev headers.)

🤖 Generated with Claude Code

Phase 14a.5b. Adds 29 env::gossamer_* host imports and 1 env::cap_token
to the wasmtime launcher MVP from 14a.5a. The guest can now talk to
libgossamer (open windows, navigate, run watcher, spawn shells, read
conf, etc.) through these bridges.

New file cli/launcher/src/bridges.zig (~470 LOC):

  • extern fn declarations for all 29 libgossamer symbols (matching
    src/interface/ffi/src/*.zig exports).
  • marshalling helpers: dupeZGuestString (wasm ptr/len → host
    null-terminated dupe) and writeCStringToGuest (host C string →
    guest buffer, WASI-style write-into-caller-buffer pattern).
  • One bridge callback per symbol. String inputs unmarshal via
    dupeZGuestString; string outputs use writeCStringToGuest. Opaque
    handles (watcher, shell child, conf) cross the wasm boundary as
    i64 (bit-cast u64 = host pointer).
  • A static Imports table registered with the wasmtime linker.
  • grantCaps(host_env) eager-grants tokens for ResourceKind 0
    (FileSystem), 1 (Network), 2 (Shell). The guest reads them via
    env::cap_token(kind).

Updates cli/launcher/src/main.zig:

  • HostEnv gains cap_tokens: [6]u64 keyed by ResourceKind.
  • HostEnv, ImportSpec, guestSlice, and the @cImport bound `c`
    are now `pub` so bridges.zig can use them.
  • main() calls bridges.grantCaps() and registers bridges.Imports
    alongside the 5 baseline imports.

Updates cli/launcher/build.zig to link libgossamer (added
addLibraryPath + addRPath pointing at ../../src/interface/ffi/zig-out
and linkSystemLibrary("gossamer")).

Added cli/launcher/src/hello-bridges.wat — smoke test calling
gossamer_version_to into a guest buffer and printing via print_string.
Validated with wasm-tools parse (208 bytes).

Coverage:

  String-out      gossamer_version_to / build_info_to / last_error_to
  Webview         create_ex / navigate / load_html / channel_open /
                  set_csp / set_title / registry_add / run
  Groove          groove_discover / groove_status
  Watcher         watcher_start / watcher_stop
  Shell           shell_spawn / shell_kill
  Filesystem      fs_read_text / fs_write_text / fs_exists / fs_mkdir_p /
                  fs_copy_file
  Conf            conf_load / get_string / get_int / get_bool / has / free
  Caps            cap_token

Explicitly out of scope:

  • The 27 gossamer_channel_bind IPC handlers (window_minimize, group_*,
    transmute, etc. — currently in cli/src/main.zig) DO NOT bridge
    through wasm. They live native-side. A follow-up moves them into
    libgossamer's channel_open default-handler set.
  • Building cli.wasm from .eph source — 14a.5c.
  • cli.wasm discovery at /usr/share/gossamer — 14a.5c.

Verified:

  • zig ast-check on main.zig, bridges.zig, build.zig — clean.
  • wasm-tools parse on hello-bridges.wat — 208-byte valid module.
  • Full link requires libwasmtime (14a.5a dep) AND libgossamer built
    in ../../src/interface/ffi/zig-out; both deferred to local /
    CI build environment.

Builds on top of 14a.5a (gossamer #28); PR base set accordingly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 31 issues detected

Severity Count
🔴 Critical 11
🟠 High 4
🟡 Medium 16

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Issue in quality.yml",
    "type": "missing_workflow",
    "file": "quality.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in security-policy.yml",
    "type": "missing_workflow",
    "file": "security-policy.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Action actions/upload-artifact@v4 needs attention",
    "type": "unpinned_action",
    "file": "release.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action actions/download-artifact@v4 needs attention",
    "type": "unpinned_action",
    "file": "release.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "believe_me undermines formal verification (1 occurrences, CWE-704)",
    "type": "believe_me",
    "file": "/home/runner/work/gossamer/gossamer/src/interface/abi/IPCDispatch.idr",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "believe_me undermines formal verification (1 occurrences, CWE-704)",
    "type": "believe_me",
    "file": "/home/runner/work/gossamer/gossamer/src/interface/abi/ResourceCleanup.idr",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "believe_me undermines formal verification (1 occurrences, CWE-704)",
    "type": "believe_me",
    "file": "/home/runner/work/gossamer/gossamer/src/interface/abi/GrooveTermination.idr",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "believe_me undermines formal verification (1 occurrences, CWE-704)",
    "type": "believe_me",
    "file": "/home/runner/work/gossamer/gossamer/src/interface/abi/HandleLinearity.idr",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "believe_me undermines formal verification (1 occurrences, CWE-704)",
    "type": "believe_me",
    "file": "/home/runner/work/gossamer/gossamer/src/interface/abi/WindowStateMachine.idr",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath hyperpolymath merged commit 3c1fc07 into main May 20, 2026
17 of 19 checks passed
hyperpolymath added a commit that referenced this pull request May 20, 2026
Recovery PR 3/4. Same content as orphaned PR #33 — a dispatcher
that exercises sum types, pattern matching, multi-branch
conditionals, let bindings, multiple FFI externs, and real calls
into libgossamer through the 14a.5b bridges (now on main via #35).

Architecture:

  • pub data Subcommand = NoArg | InfoOrVersion | DevOrRunOrBuild |
    Init | Bundle | TooMany — classifies the run by argv_count.
  • dispatchCode(s) match-lowers each variant to a stable status
    code (100..900).
  • grooveProbe() calls env::gossamer_groove_discover — proves the
    14a.5b libgossamer bridge surface is reachable from Ephapax.
  • statusCode() composes dispatch + clamped-groove-count into a
    single I32 the harness reads back.
  • main() prints statusCode() and returns.

Compiles to 1514-byte cli.wasm. 5 host imports (all in the launcher's
bridge surface from 14a.5a/5b): print_i32, argv_count, argv_arg_len,
gossamer_groove_discover, gossamer_groove_status. Plus the 2 always-on
ephapax baseline imports.

Documented v2-grammar limits hit during this work (all three
addressed in the next recovery PR via launcher-side helpers):

  1. No linear-memory reads from user code (no argv[1] string match).
  2. No I64 literal (cap_token == 0 can't typecheck).
  3. String-typed externs lower to opaque i32 handles, not (ptr, len).

Subcommand dispatch via argv_count is the workaround in this PR;
the conventional argv[0]-match shape lands in recovery PR 4/4 with
the String FFI bridges.

Recovery PR 3/4. Lands on top of:
  • #35 (14a.5b libgossamer bridges) — MERGED
  • #37 (14a.5c build integration) — open, will merge before this

This PR's Main.eph is dead code without #37's build.zig ephapax
compile step. Merge order matters: #37 first, then this PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 20, 2026
Recovery PR 3/4. Same content as orphaned PR #33 — a dispatcher
that exercises sum types, pattern matching, multi-branch
conditionals, let bindings, multiple FFI externs, and real calls
into libgossamer through the 14a.5b bridges (now on main via #35).

Architecture:

  • pub data Subcommand = NoArg | InfoOrVersion | DevOrRunOrBuild |
    Init | Bundle | TooMany — classifies the run by argv_count.
  • dispatchCode(s) match-lowers each variant to a stable status
    code (100..900).
  • grooveProbe() calls env::gossamer_groove_discover — proves the
    14a.5b libgossamer bridge surface is reachable from Ephapax.
  • statusCode() composes dispatch + clamped-groove-count into a
    single I32 the harness reads back.
  • main() prints statusCode() and returns.

Compiles to 1514-byte cli.wasm. 5 host imports (all in the launcher's
bridge surface from 14a.5a/5b): print_i32, argv_count, argv_arg_len,
gossamer_groove_discover, gossamer_groove_status. Plus the 2 always-on
ephapax baseline imports.

Documented v2-grammar limits hit during this work (all three
addressed in the next recovery PR via launcher-side helpers):

  1. No linear-memory reads from user code (no argv[1] string match).
  2. No I64 literal (cap_token == 0 can't typecheck).
  3. String-typed externs lower to opaque i32 handles, not (ptr, len).

Subcommand dispatch via argv_count is the workaround in this PR;
the conventional argv[0]-match shape lands in recovery PR 4/4 with
the String FFI bridges.

Recovery PR 3/4. Lands on top of:
  • #35 (14a.5b libgossamer bridges) — MERGED
  • #37 (14a.5c build integration) — open, will merge before this

This PR's Main.eph is dead code without #37's build.zig ephapax
compile step. Merge order matters: #37 first, then this PR.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 20, 2026
…-targets PR #34 at main) (#39)

* feat(cli): Main.eph subcommand dispatcher in v2-grammar Ephapax

Recovery PR 3/4. Same content as orphaned PR #33 — a dispatcher
that exercises sum types, pattern matching, multi-branch
conditionals, let bindings, multiple FFI externs, and real calls
into libgossamer through the 14a.5b bridges (now on main via #35).

Architecture:

  • pub data Subcommand = NoArg | InfoOrVersion | DevOrRunOrBuild |
    Init | Bundle | TooMany — classifies the run by argv_count.
  • dispatchCode(s) match-lowers each variant to a stable status
    code (100..900).
  • grooveProbe() calls env::gossamer_groove_discover — proves the
    14a.5b libgossamer bridge surface is reachable from Ephapax.
  • statusCode() composes dispatch + clamped-groove-count into a
    single I32 the harness reads back.
  • main() prints statusCode() and returns.

Compiles to 1514-byte cli.wasm. 5 host imports (all in the launcher's
bridge surface from 14a.5a/5b): print_i32, argv_count, argv_arg_len,
gossamer_groove_discover, gossamer_groove_status. Plus the 2 always-on
ephapax baseline imports.

Documented v2-grammar limits hit during this work (all three
addressed in the next recovery PR via launcher-side helpers):

  1. No linear-memory reads from user code (no argv[1] string match).
  2. No I64 literal (cap_token == 0 can't typecheck).
  3. String-typed externs lower to opaque i32 handles, not (ptr, len).

Subcommand dispatch via argv_count is the workaround in this PR;
the conventional argv[0]-match shape lands in recovery PR 4/4 with
the String FFI bridges.

Recovery PR 3/4. Lands on top of:
  • #35 (14a.5b libgossamer bridges) — MERGED
  • #37 (14a.5c build integration) — open, will merge before this

This PR's Main.eph is dead code without #37's build.zig ephapax
compile step. Merge order matters: #37 first, then this PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cli,launcher): Ephapax String FFI + subcommand-name dispatch

Resolves the three v2-grammar limits documented in the previous
Main.eph commit by adding three small launcher bridges and using them
to upgrade the dispatcher from argv-count classification to real
argv[0] subcommand-name matching.

Launcher bridges added (cli/launcher/src/bridges.zig):

  • ephStringSlice(env, handle) helper — walks Ephapax's String layout
    (8-byte header at handle: { data_ptr: i32, len: i32 }) to recover
    the underlying byte slice from a single i32 String handle.

  • env::say_string(s: String) -> ()
      Takes an Ephapax String handle, prints its bytes to stderr.
      Mirrors the baseline env::print_string but accepts the high-level
      String type so .eph code calls it with a literal:
        say_string("hello")

  • env::argv_eq_string(idx: I32, literal: String) -> I32
      Returns 1 if argv[idx] equals the literal byte-for-byte.
      Unblocks subcommand-name dispatch entirely.

  • env::i64_is_zero(n: I64) -> I32
      Works around v2-grammar Ephapax's lack of an i64 literal
      (integer literals always parse as I32 unless > i32::MAX). Now
      cap_token comparison + opaque-handle nullability checks
      typecheck.

Main.eph upgrade (cli/src/Main.eph):

  • Subcommand classifier now matches argv[0] against the real names:
    version / info / dev / build / run / bundle / init. 9-variant
    sum type covers all + NoArg + Unknown.

  • Per-subcommand `announce(s: Subcommand)` prints a recognisable
    banner via say_string — proves the new String-aware bridge round-
    trips Ephapax literals through linear memory back to host stderr.

  • capsLive() now actually runs (used to be stubbed out) — calls
    i64_is_zero against the eager-granted FileSystem token.

  • Composite statusCode = dispatch + (grooves*10) + caps.
    e.g. `gossamer dev` with 2 grooves and caps OK -> 221.

Compiles to 3121-byte cli.wasm (up from 1514). 9 user imports plus
2 ephapax baseline — all 9 match the launcher's bridge surface
signatures byte-for-byte (verified via wasm Import section dump).

What this unlocks:

  Subcommand dispatch with the conventional argv[0]-match shape is
  the bedrock the rest of the port needs. The remaining gap is just
  filling in the per-subcommand bodies — opening webviews, running
  watchers, calling shell, etc. — which is now an "exhaustive bridge
  coverage" task rather than a "does this approach work at all"
  question.

Verified:

  • zig ast-check on bridges.zig — clean.
  • ephapax compile cli/src/Main.eph -o /tmp/cli-v3.wasm — 3121 bytes,
    9 user imports + 2 baseline, signatures match launcher bridges:
        env::say_string         type_idx=1   (i32) -> ()
        env::argv_eq_string     type_idx=3   (i32, i32) -> i32
        env::cap_token          type_idx=7   (i32) -> i64
        env::i64_is_zero        type_idx=8   (i64) -> i32

Stacked on #33 (the Main.eph baseline). Base set accordingly. When
both this and #33 land, only the per-subcommand bodies remain for the
final-port PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath hyperpolymath deleted the feat/recover-14a-5b-bridges branch May 20, 2026 13:08
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