Skip to content

recover(launcher): 14a.5c build integration + cli.wasm discovery (re-targets PR #30 at main)#37

Merged
hyperpolymath merged 1 commit into
mainfrom
feat/recover-14a-5c-build-integration
May 20, 2026
Merged

recover(launcher): 14a.5c build integration + cli.wasm discovery (re-targets PR #30 at main)#37
hyperpolymath merged 1 commit into
mainfrom
feat/recover-14a-5c-build-integration

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Why this PR exists

PR #30 ("feat(cli/launcher): build integration + cli.wasm discovery (14a.5c)") shows as MERGED on GitHub but its commits never landed on main — same stacked-PR-into-feature-branch trap as #29 (rescued via just-merged #35).

This is recovery PR 2 of 4. PR 1/4 (#35) just merged; this one stacks on the same recovery path.

What this PR lands

File Change
`cli/launcher/build.zig` +30 LOC — `ephapax compile` build step + `cli.wasm` install at `share/gossamer/cli.wasm`
`cli/launcher/src/main.zig` +50 LOC — `findCliWasm()` discovery (env var → exe-relative → /usr/local → /usr)
`cli/launcher/README.adoc` +57 LOC — documents build workflow + search order
`cli/src/Main.eph` (new) ~30 LOC — placeholder Ephapax entry point (calls argv_count + print_i32)

Discovery search order

  1. `$GOSSAMER_WASM` env var (explicit override)
  2. `<exe_dir>/../share/gossamer/cli.wasm` (install-prefix-relative)
  3. `/usr/local/share/gossamer/cli.wasm`
  4. `/usr/share/gossamer/cli.wasm`

Build workflow (post-merge)

```sh
export EPHAPAX=$HOME/dev/repos/ephapax/target/debug/ephapax
cd cli/launcher
zig build install --prefix /usr/local

→ /usr/local/bin/gossamer-launcher

→ /usr/local/share/gossamer/cli.wasm

gossamer-launcher # discovers + runs cli.wasm
gossamer-launcher dev # passes 'dev' to the guest
```

Verification

What lands after this

Recovery PR 3/4 — Main.eph dispatcher upgrade (orphaned PR #33).
Recovery PR 4/4 — String FFI + argv[0]-name dispatch (orphaned PR #34).

🤖 Generated with Claude Code

Phase 14a.5c. Closes the prep arc: `zig build install` now produces both
the launcher binary AND a compiled cli.wasm, placed under the install
prefix's share/gossamer/. The launcher discovers cli.wasm at runtime via
a standard search order.

New file cli/src/Main.eph (~25 lines):

  Placeholder entry point. Declares env::print_i32 and env::argv_count
  as extern "env" fns and defines `fn main(): Unit = print_i32(argv_count())`.
  Compiles cleanly via ephapax compile to a 715-byte .wasm with the
  expected `main` export. Real CLI subcommand dispatch lands in #15.

cli/launcher/build.zig (+30 LOC):

  • Reads $EPHAPAX env var (defaults to `ephapax` on PATH) so dev
    workflows can point at a sibling repo's debug build.
  • addSystemCommand wraps `ephapax compile cli/src/Main.eph -o cli.wasm`.
    addOutputFileArg makes the .wasm a tracked build artefact.
  • addInstallFileWithDir places it at `share/gossamer/cli.wasm` under
    the install prefix.
  • Run step sets GOSSAMER_WASM to the install location so `zig build
    run -- args...` uses the freshly-built wasm.

cli/launcher/src/main.zig (+50 LOC):

  • New findCliWasm(allocator) checks (in order): $GOSSAMER_WASM env
    var, <exe_dir>/../share/gossamer/cli.wasm, /usr/local/share/gossamer/
    cli.wasm, /usr/share/gossamer/cli.wasm. Returns null if none exist.
  • main() falls through to discovery when no .wasm argument is given
    (argv[1] must end in .wasm to be treated as an explicit override).
    The guest_argv slice shifts by 1 or 2 depending on whether a wasm
    path was passed, so the guest sees exactly the user's intended
    arguments regardless of invocation shape.
  • Usage message updated to document the search order.

cli/launcher/README.adoc:

  • Documents the new build workflow (EPHAPAX env var, zig build install
    --prefix, post-install discovery).
  • Documents the 4-step search order.
  • Notes that the launcher is still installed as `gossamer-launcher`
    alongside the existing `gossamer` native binary — the rename + IPC
    handler migration is #15's job.

What's NOT in this PR (intentionally):

  • Renaming gossamer-launcher → gossamer. Requires migrating the 27
    gossamer_channel_bind IPC handlers out of cli/src/main.zig first.
  • Filling in Main.eph with real CLI subcommand dispatch (dev / build /
    bundle / run / init / info). That's #15.

Verified:

  • zig ast-check on main.zig + build.zig — clean.
  • ephapax compile cli/src/Main.eph → 715-byte .wasm with correct
    imports (env::print_i32, env::argv_count, baseline ephapax) and
    `main` export.
  • Wasm module inspection confirms imports + exports match the
    launcher's expectations.

Stacked on 14a.5b (gossamer #29). When 14a.5b merges, this PR's base
auto-rebases to main.

This concludes Phase 14a (the prep arc). #15 — the actual port of
cli/src/main.zig logic into Main.eph — can now proceed against a known-
working launcher + bridge surface.

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 e78dc28 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-5c-build-integration 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