recover(launcher): 14a.5c build integration + cli.wasm discovery (re-targets PR #30 at main)#37
Merged
Conversation
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>
🔍 Hypatia Security ScanFindings: 31 issues detected
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 |
This was referenced May 20, 2026
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>
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.
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
Discovery search order
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