Skip to content

F# codegen: behavioural run-step under TypeLoadMode.Static (GH-2969)#2987

Merged
jeremydmiller merged 1 commit into
mainfrom
feat-2969-fsharp-behavioural-runstep
May 29, 2026
Merged

F# codegen: behavioural run-step under TypeLoadMode.Static (GH-2969)#2987
jeremydmiller merged 1 commit into
mainfrom
feat-2969-fsharp-behavioural-runstep

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

Part of the F# code-generation audit (#2969). Every slice so far is a compile-gate (proves the generated F# compiles). This adds the missing run-verified harness: it boots a real Wolverine host in TypeLoadMode.Static against a pre-generated F# handler adapter and asserts the handler actually executes.

How it works

  • Wolverine.Behavioural.FSharpApp (F#) is a self-contained application assembly carrying BOTH an F# handler (BehaviouralPingHandler, which records to an in-process sink) AND its pre-generated F# adapter (Generated.fs).
  • Wolverine.Behavioural.FSharpTests:
    • generated_fsharp_handler_runs_under_static_load — boots a host with TypeLoadMode.Static + ApplicationAssembly = the F# app, sends a BehaviouralPing(42), and asserts the sink recorded 42. Wolverine loads the pre-generated F# MessageHandler by name (BehaviouralPingHandler1244766258) from the app's exported types and executes it — no Roslyn at runtime.
    • generated_fsharp_regenerates_and_compiles — regenerates Generated.fs from the shared BehaviouralCodegen.Configure and dotnet builds the app, so the committed F# adapter can't silently drift from the codegen output.

Key mechanics discovered

  • Set opts.ApplicationAssembly (not just CodeGeneration.ApplicationAssembly) — it cascades to CodeGeneration.ApplicationAssembly and survives bootstrap (the re-establish step is gated on _applicationAssembly == null), so static load scans the F# app, not the test assembly.
  • The generated F# handler type is public (exported), so assembly.ExportedTypes.FirstOrDefault(x => x.Name == TypeName) finds it, and QuickBuild instantiates it via its primary constructor — F# generated handlers slot straight into Wolverine's static loader.
  • A shared config between generation and runtime keeps the generated type-name hash in sync (a drift throws ExpectedTypeMissingException at startup — a loud signal).

Wire-up & verification

  • App + test added to wolverine_fsharp.slnx; fsharp.yml runs the behavioural run-step as its own sequential step.
  • Both facts green; full wolverine_fsharp.slnx builds clean. No production-code changes (pure test/harness), so wolverine.slnx is unaffected.

This upgrades the audit's strongest guarantee from "the generated F# compiles" to "the generated F# compiles and runs," and is the template for run-verifying any future slice.

🤖 Generated with Claude Code

…H-2969)

Adds the run-verified harness the audit was missing: every prior slice is a
compile-gate (proves the generated F# compiles); this proves it RUNS.

Wolverine.Behavioural.FSharpApp (F#) is a self-contained "application assembly"
carrying BOTH an F# handler (BehaviouralPingHandler, records to an in-process
sink) AND its pre-generated F# adapter (Generated.fs). Wolverine.Behavioural.FSharpTests:
- generated_fsharp_handler_runs_under_static_load: boots a real Wolverine host with
  TypeLoadMode.Static + ApplicationAssembly = the F# app, sends a BehaviouralPing, and
  asserts the sink recorded it — i.e. Wolverine loaded the pre-generated F# MessageHandler
  by name (BehaviouralPingHandler<hash>) and executed it, no Roslyn at runtime.
- generated_fsharp_regenerates_and_compiles: regenerates Generated.fs from the SHARED
  BehaviouralCodegen.Configure and dotnet-builds the app, so the committed F# adapter
  can't silently drift from the codegen output (the shared config keeps the generated
  type-name hash in sync between generation and the runtime host).

Key mechanics: set opts.ApplicationAssembly (cascades to CodeGeneration.ApplicationAssembly
and survives bootstrap because _applicationAssembly is non-null) so static load scans the
F# app, not the test assembly; the generated F# type is public (exported) and QuickBuild
instantiates it via its primary constructor.

Wire-up: app + test added to wolverine_fsharp.slnx; fsharp.yml runs the behavioural
run-step as its own sequential step. No production-code changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeremydmiller jeremydmiller merged commit df5cf00 into main May 29, 2026
24 checks passed
This was referenced Jun 1, 2026
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