Novolis Docs
novolis-codegen / specs/binding-codegen-library/initial-idea.md

Binding CodeGen Library — Specification (draft)

dotnetcodegennovolis

Status: draft Reference consumer: novolis-raylib Target repo: novolis-codegen

This document specifies a reusable C# library for manifest-driven native binding generation: fetch sources, verify manifests, emit interop/façade C#, apply Roslyn post-processing hooks, and pack native binaries. The design is 1:1 with what novolis-raylib does today, expressed as composable fragments and explicit merge points so “Raylib + ImGui → one Bindings assembly” is ergonomic without losing the current emit semantics.


1. Goals

GoalMeaning
1:1 parityA RaylibBindingProject definition reproduces all eight manifests, eight emit targets, three hook phases, and pipeline step_06 output paths exactly.
Library ergonomicsManifests authored in C# with IntelliSense; fluent merge for optional add-ons (Raygui); JSON as derived artifact for drift/agents.
Roslyn as adaptation layerString emitters produce baseline syntax trees; hooks rewrite trees for docs, inlining, cross-cutting injection — not second emitters.
Incremental pipelineFingerprinted steps (result.json, input SHA256, skip-if-up-to-date) remain first-class; library supplies the kernel, consumers supply domain steps.

2. Reference inventory (novolis-raylib as-is)

2.1 Pipeline steps

StepIdRole
01step_01_sourceFetch raylib prebuilts, raygui header, raylib-cimgui into artifacts/ (versions.json)
02step_02_nativeCMake build shims; copy DLLs to artifacts/
03step_03_verify_manifestManifest imports vs fetched raylib.h
04step_04_enrich_docsFill façade summaries from headers (writes manifests)
05step_05_verify_docsFail on missing façade docs
06step_06_codegenEmit committed *.g.cs
07step_07_driftgit diff on manifests + generated C#
08step_08_buildRelease build Bindings + Runtime

Profiles: maintainer, generate, ci-codegen, agent-verify.

2.2 Manifest files → generated outputs (authoritative map)

ManifestSchema kindEmitterPhaseOutput pathPartial class
raylib-exports.manifest.jsonInteropRaylibInteropEmitterInteropsrc/Novolis.Raylib.Bindings/Interop/Raylib6Native.g.csRaylib6Native
imgui-exports.manifest.jsonShim exportsImguiInteropEmitterImGuisrc/Novolis.Raylib.Bindings/Interop/ImguiShimExports.g.csImguiShimExports
raylib-debug.manifest.jsonDebug configRaylibDebugHooksEmitterDebugsrc/Novolis.Raylib.Bindings/Interop/RaylibDebugFrameHooks.g.csRaylibDebugFrameHooks
facades.manifest.jsonFaçade typesFacadeEmitterFacadesrc/Novolis.Raylib.Runtime/{folder}/{Name}.g.csper type
hud.manifest.jsonFaçade typesFacadeEmitterFacadesrc/Novolis.Raylib.Runtime/Hud/Hud.g.csHud
gui.manifest.jsonFaçade typesFacadeEmitterFacadesrc/Novolis.Raylib.Runtime/Gui/Gui.g.csGui
raygui-exports.manifest.jsonShim exportsRayguiInteropEmitterRayguisrc/Novolis.Raylib.Raygui/Interop/RayguiShimExports.g.csRayguiShimExports
raygui.manifest.jsonFaçade typesFacadeEmitterFacadesrc/Novolis.Raylib.Raygui/RayGui/RayGui.g.csRayGui

Façade types from `facades.manifest.json` today: Graphics, Window, Input, World, Textures, Time, AudioDevice (folders mirror names under Runtime/).

2.3 Emit strategies (do not flatten)

StrategyUsed byGenerated shape
LibraryImportraylib-exports[LibraryImport] partial static methods on Raylib6Native; blittable structs; DisableRuntimeMarshalling policy
DynamicExportsimgui-exports, raygui-exportsFunction-pointer fields + TryBindShim(nint module) via NativeLibrary.GetExport
DebugHooksraylib-debug#if DEBUG events + capture helpers referencing symbols from interop manifest
FacadeForwardfaçade manifestspublic static partial class with expression-bodied forwards to interop/shim

Raylib + ImGui share `Novolis.Raylib.Bindings` and namespace `Novolis.Raylib.Interop` but remain separate partial classes — merge point orchestrates inclusion, not class merging.

2.4 Roslyn hooks (shipped today)

HookOrderPhaseBehavior
AnnotateLibraryImportHook10InteropXML /// <summary> on [LibraryImport] methods from manifest description
InjectEndDrawingNotifyHook20FacadeRewrites Graphics.EndDrawing body to notify debug hooks + frame capture hub
FacadeInliningHook30Facade[MethodImpl(AggressiveInlining)] on expression-bodied façade methods when policy says so

Emit pipeline per file:

string source = Emitter.Emit(manifestBytes, sha256)
CompilationUnitSyntax unit = CodegenFormatter.ParseGenerated(source)
foreach hook in hooks.Where(phase matches).OrderBy(Order):
    unit = hook.Transform(unit, context)
string file = Format(unit)   // interop: Formatter.Format; façade: NormalizeWhitespace
File.WriteAllText(outputPath, file)

Context carries: RepoRoot, Phase, OutputPath, ManifestPath, ManifestSha256, RegenerateHint, ImportDescriptions, FacadeTypeName, FacadeMethodImpl.

2.5 Native pack (Bindings csproj today)

BinarySourceLink name
raylib.dllstep_01 prebuiltraylib.dll
novolis_raylib_trace.dllstep_02 / native outnovolis_raylib_trace.dll
novolis_imgui.dllstep_02 / native outnovolis_imgui.dll
novolis_raygui.dllstep_02 (optional IncludeRaygui)novolis_raygui.dll

Raygui interop/façade outputs live in `Novolis.Raylib.Raygui` add-on assembly, not Bindings — optional merge branch.


3. Library package layout (novolis-codegen)

Novolis.CodeGen.Pipeline          Step kernel: IPipelineStep, runner, skip, result.json
Novolis.CodeGen.Bindings          Manifest fragments, merge plan, serializers, emit orchestration
Novolis.CodeGen.Bindings.Roslyn   Parse/format, ICodegenHook, discovery, rewriter helpers

Consumer (novolis-raylib):

Novolis.Raylib.Manifests          C# manifest definitions + RaylibBindingProject (1:1)
Novolis.Raylib.CodeGen              Domain emitters (templates), verify, enrich, suggest
Novolis.Raylib.CodeGen.Hooks        Domain Roslyn hooks
Novolis.Raylib.Pipeline             Domain steps (source, native, drift, build)

Codegen projects stay unpublished; only Novolis.CodeGen.* packages ship on NuGet.


4. Manifest model (1:1 JSON schemas)

Each manifest file maps to a typed fragment with stable Id. Fragments serialize to the exact JSON shape already committed in raylib (round-trip required for drift).

4.1 Fragment kinds

public enum FragmentKind
{
    InteropExports,     // raylib-exports.manifest.json
    ShimExports,        // imgui-exports, raygui-exports
    DebugConfig,        // raylib-debug.manifest.json
    FacadeTypes,        // facades, hud, gui, raygui manifests
    NativeArtifacts,    // logical ref to DLLs (not a JSON file today)
    SourceVersions,     // versions.json
}

4.2 Interop exports fragment

Mirrors RaylibManifest / RaylibManifestModels:

public sealed record InteropExportsFragment(
    string Id,                          // "raylib6"
    int SchemaVersion,
    string? Header,                     // metadata only
    string? Description,
    string DllName,                     // "raylib"
    InteropPolicy Policy,
    IReadOnlyList<InteropStruct> Structs,
    IReadOnlyList<InteropImport> Imports);

public sealed record InteropPolicy(
    IReadOnlyList<string> SuppressGcTransitionByTemplate,
    IReadOnlyList<string> NeverSuppressGcTransition,
    string? FacadeMethodImpl,           // "AggressiveInlining"
    bool UseDisableRuntimeMarshalling);

public sealed record InteropImport(
    string Name,
    string Template,                    // e.g. "void_void" — see Template catalog §4.6
    string? Description,
    bool? SuppressGcTransition);

C# authoring:

InteropExportsFragment.Create("raylib6")
    .SchemaVersion(2)
    .Header(Vendor.Raylib6.Header)
    .Dll("raylib")
    .Policy(p => p
        .SuppressGcTransitionByTemplate(
            Template.VoidVoid, Template.VoidColor, /* … full list from manifest … */)
        .NeverSuppress("MemFree", "LoadTexture")
        .FacadeMethodImpl("AggressiveInlining")
        .DisableRuntimeMarshalling())
    .Struct("Raylib6NativeImage", s => s
        .Field("Data", "nint")
        .Field("Width", "int")
        .Field("Height", "int")
        .Field("Mipmaps", "int")
        .Field("Format", "int"))
    .Import("BeginDrawing", Template.VoidVoid)
    .Import("EndDrawing", Template.VoidVoid)
    // bulk bootstrap: .ImportFromJson(path) or .SuggestFromHeader(header, filter)
    .Build();

4.3 Shim exports fragment

Mirrors ImguiManifest / RayguiManifest:

public sealed record ShimExportsFragment(
    string Id,                          // "imgui" | "raygui"
    int SchemaVersion,
    string? Header,
    string? Description,
    string ModuleFileName,              // "novolis_imgui.dll" — used by NativePack, not in JSON today
    IReadOnlyList<ShimExport> Exports);

public sealed record ShimExport(string Export, string Template);

ImGui example (1:1 with imgui-exports.manifest.json):

ShimExportsFragment.Create("imgui")
    .SchemaVersion(1)
    .Header(Vendor.Cimgui.Header)
    .Module("novolis_imgui")
    .Export("novolis_rlimgui_setup", Template.VoidInt)
    .Export("novolis_igBegin", Template.IntUtf8PtrIntInt)
    // …
    .Build();

4.4 Debug config fragment

Mirrors raylib-debug.manifest.json:

public sealed record DebugConfigFragment(
    string Id,
    int SchemaVersion,
    string? Description,
    string NotifyAfterNativeCall,
    string FrameHubNotifyAfter,
    string CaptureEnvVar,
    string CapturePngFileType,
    DebugSymbolMap Symbols);

public sealed record DebugSymbolMap(
    string LoadImageFromScreen,
    string ExportImageToMemory,
    string UnloadImage,
    string MemFree);

Symbols reference import names from the interop fragment (Raylib6Native.{name}).

4.5 Façade types fragment

Mirrors FacadesManifest / FacadeTypeDefinition:

public sealed record FacadeTypesFragment(
    string Id,                          // "facades" | "hud" | "gui" | "raygui"
    IReadOnlyList<FacadeType> Types);

public sealed record FacadeType(
    string Name,
    string Namespace,
    string Folder,
    string? TypeSummary,
    IReadOnlyList<string> Usings,
    IReadOnlyList<FacadeMethod> Methods);

public sealed record FacadeMethod(
    string Name,
    string Signature,                   // "void BeginDrawing()"
    string Body,                        // "Raylib6Native.BeginDrawing()"
    string? Summary);

Façade bodies are opaque C# snippets today — they cross-reference merged interop/shim types by name. Merge point AllowCrossReference() documents that dependency.

4.6 Template catalog

Templates are the contract between manifest and emitter switch arms. Library exposes them as constants that serialize to existing strings:

public static class Template
{
    public static readonly TemplateId VoidVoid = "void_void";
    public static readonly TemplateId VoidColor = "void_color";
    public static readonly TemplateId IntUtf8PtrIntInt = "int_utf8_ptrint_int";
    // … one per case arm in RaylibInteropEmitter + ImguiInteropEmitter
}

Custom templates remain possible via implicit conversion from string for domain extensions.

4.7 JSON round-trip

public interface IManifestFragment
{
    string Id { get; }
    FragmentKind Kind { get; }
    string FileName { get; }            // e.g. "raylib-exports.manifest.json"
    JsonDocument ToJson();              // canonical shape for drift
    byte[] ToUtf8Bytes() => …;
    string Sha256Hex() => …;
}

// Bootstrap from committed files during migration:
InteropExportsFragment.FromJson("codegen/pipeline/raylib6/raylib-exports.manifest.json");

Authoring policy: C# is source of truth; SerializeManifests(dir) writes JSON before drift check. During transition, hand-edited JSON can be imported once into C# definitions.


5. Merge points

Merge points are named composition steps in a BindingProject. They do not imply merging C# types unless EmitStrategy explicitly requests it.

5.1 Merge point catalog

Merge pointInput fragmentsOutputRaylib mapping
BindingsInteropExports, ShimExports, DebugConfig*.g.cs under Bindings Interop/Raylib6Native + ImguiShimExports + RaylibDebugFrameHooks in one assembly
Facades.CoreFacadeTypes (facades, hud, gui)Runtime/**/*.g.csCore runtime façades
Facades.AddOnFacadeTypes (raygui) + ShimExports (raygui)Raygui/**/*.g.csOptional add-on assembly
NativePackNativeArtifactsMSBuild item metadata / packagingBindings csproj copy rules
VerifyInteropExports + vendor headerpass/failstep_03
EnrichDocsFacadeTypes + headersupdated façade JSON/C#step_04

5.2 Bindings merge (Raylib + ImGui)

BindingsMerge.Create()
    .TargetAssembly("Novolis.Raylib.Bindings")
    .TargetNamespace("Novolis.Raylib.Interop")
    .Include("raylib6", new EmitTarget(
        ClassName: "Raylib6Native",
        Strategy: EmitStrategy.LibraryImport,
        RelativePath: "Interop/Raylib6Native.g.cs",
        Phase: EmitPhase.Interop))
    .Include("imgui", new EmitTarget(
        ClassName: "ImguiShimExports",
        Strategy: EmitStrategy.DynamicExports,
        RelativePath: "Interop/ImguiShimExports.g.cs",
        Phase: EmitPhase.ImGui))
    .Include("raylib-debug", new EmitTarget(
        ClassName: "RaylibDebugFrameHooks",
        Strategy: EmitStrategy.DebugHooks,
        RelativePath: "Interop/RaylibDebugFrameHooks.g.cs",
        Phase: EmitPhase.Debug,
        DependsOn: ["raylib6"]))   // symbol names from interop imports
    .ConflictPolicy(ConflictPolicy.FailOnDuplicateTypeName)
    .Hooks(EmitPhase.Interop, EmitPhase.ImGui, EmitPhase.Debug);

Optional Raygui branch (separate assembly — matches today):

.When(includeRaygui, plan => plan
    .BindingsAddOn("Novolis.Raylib.Raygui")
        .Include("raygui-shim", "RayguiShimExports", EmitStrategy.DynamicExports,
            "Interop/RayguiShimExports.g.cs", EmitPhase.Raygui));

5.3 Façades merge

FacadesMerge.Create()
    .TargetRoot("src/Novolis.Raylib.Runtime")
    .Include("facades")           // Graphics, Window, Input, World, Textures, Time, AudioDevice
    .Include("hud")
    .Include("gui")                 // bodies reference ImguiShimExports
  .PolicyFrom("raylib6")            // FacadeMethodImpl → inlining hook + emitter
    .HeaderDocs(Vendor.Raylib6.Header, Vendor.Raygui.Header)
    .Hooks(EmitPhase.Facade);

5.4 BindingSurface sugar

Fluent composition without losing explicit merge semantics:

public readonly struct BindingSurface
{
    public static BindingSurface Core { get; }      // raylib6 + debug
    public static BindingSurface ImGui { get; }     // imgui shim + gui façades

    public static BindingSurface operator +(BindingSurface a, BindingSurface b)
        => BindingSurface.Merge(a, b);

    public BindingProject IntoProject(string name) => …;
}

// Ergonomic:
var bindings = (BindingSurface.Core + BindingSurface.ImGui)
    .IntoProject("Novolis.Raylib")
    .WithNativePack(NativePack.Raylib6.WithImGui());

operator+ adds merge edges to the plan graph; no file I/O until project.Emit() or pipeline step_06.


6. Full reference: RaylibBindingProject (1:1)

This is the target entrypoint replacing RaylibCodegenPipeline.GenerateBindingsOnly():

public static class RaylibBindingProject
{
    public static BindingProject Create(string repoRoot) =>
        BindingProject.Define("Novolis.Raylib", repoRoot)
            .Fragment(RaylibFragments.InteropExports)      // → raylib-exports.manifest.json
            .Fragment(RaylibFragments.ImGuiShim)           // → imgui-exports.manifest.json
            .Fragment(RaylibFragments.DebugConfig)         // → raylib-debug.manifest.json
            .Fragment(RaylibFragments.Facades)             // → facades.manifest.json
            .Fragment(RaylibFragments.Hud)                 // → hud.manifest.json
            .Fragment(RaylibFragments.Gui)                 // → gui.manifest.json
            .Fragment(RaylibFragments.RayguiShim)          // → raygui-exports.manifest.json
            .Fragment(RaylibFragments.RayguiFacade)          // → raygui.manifest.json
            .Fragment(RaylibFragments.NativePack)
            .Fragment(RaylibFragments.SourceVersions)      // → versions.json
            .Merge(plan => plan
                .Bindings(b => b
                    .Include(RaylibFragments.InteropExports, EmitTargets.Raylib6Native)
                    .Include(RaylibFragments.ImGuiShim, EmitTargets.ImguiShimExports)
                    .Include(RaylibFragments.DebugConfig, EmitTargets.DebugFrameHooks))
                .Facades(f => f
                    .Include(RaylibFragments.Facades, OutputRoot.Runtime)
                    .Include(RaylibFragments.Hud, OutputRoot.Runtime)
                    .Include(RaylibFragments.Gui, OutputRoot.Runtime)
                    .PolicyFrom(RaylibFragments.InteropExports)
                    .HeaderDocsFromSourceStep())
                .AddOn(a => a
                    .When(RaylibOptions.IncludeRaygui, branch => branch
                        .Bindings(EmitTargets.RayguiShimExports, OutputRoot.Raygui)
                        .Facades(RaylibFragments.RayguiFacade, OutputRoot.Raygui)
                        .Native(NativeArtifacts.RayguiDll)))
                .NativePack(NativeArtifacts.Raylib6Core))
            .SerializeDerivedManifests("codegen/pipeline/raylib6/")
            .RegisterHooksFromAssembly(typeof(AnnotateLibraryImportHook).Assembly)
            .Build();

    public static int Emit(string repoRoot)
    {
        var project = Create(repoRoot);
        return project.EmitAll();   // same files as step_06 CodegenOutputCatalog
    }
}

Parity check: EmitAll() output set must equal CodegenOutputCatalog.AllGeneratedFiles(repoRoot) byte-for-byte when fragments are bootstrapped from current JSON.


7. Roslyn adaptation layer (Novolis.CodeGen.Bindings.Roslyn)

Roslyn is the adaptation surface between dumb string emitters and final committed C#. The library should make hooks easy to write, test, and compose.

7.1 Core hook contract (generalized from IRaylibCodegenHook)

public interface ICodegenHook<TPhase, in TContext>
    where TPhase : struct, Enum
{
    int Order { get; }
    TPhase Phase { get; }
    CompilationUnitSyntax Transform(CompilationUnitSyntax unit, TContext context);
}

Raylib consumer:

public interface IRaylibCodegenHook : ICodegenHook<EmitPhase, RaylibCodegenContext> { }

7.2 Emit writer (centralizes today’s WriteUnit)

public sealed class RoslynEmitWriter<TPhase, TContext>
    where TPhase : struct, Enum
{
    public void Write(
        string rawSource,
        TContext context,
        TPhase phase,
        string outputPath,
        IReadOnlyList<ICodegenHook<TPhase, TContext>> hooks,
        FormatPolicy format)
    {
        var unit = CSharpSyntaxTree.ParseText(rawSource).GetRoot() as CompilationUnitSyntax;
        foreach (var hook in hooks.Where(h => h.Phase.Equals(phase)).OrderBy(h => h.Order))
            unit = hook.Transform(unit!, context);
        var text = format.Apply(unit!);
        WriteFile(outputPath, text, context);
    }
}

FormatPolicy: RoslynFormatter (interop) vs NormalizeWhitespace (façades) — matches current behavior.

7.3 Rewriter helpers (library-provided)

Reduce boilerplate for common binding adaptations:

public static class SyntaxRewriters
{
    public static CompilationUnitSyntax AddXmlDocToMethods(
        CompilationUnitSyntax unit,
        Func<MethodDeclarationSyntax, string?> summaryLookup);

    public static CompilationUnitSyntax AddAttributeToMethods(
        CompilationUnitSyntax unit,
        Func<MethodDeclarationSyntax, AttributeListSyntax?> attributeFactory);

    public static CompilationUnitSyntax RewriteMethodBody(
        CompilationUnitSyntax unit,
        string typeName,
        string methodName,
        Func<BlockSyntax?> bodyFactory);

    public static CompilationUnitSyntax EnsureUsing(
        CompilationUnitSyntax unit, string namespaceName);
}

AnnotateLibraryImportHook and FacadeInliningHook become thin wrappers over these helpers. InjectEndDrawingNotifyHook uses RewriteMethodBody for Graphics.EndDrawing.

7.4 Hook discovery

Generalize HookDiscovery:

public static class HookDiscovery
{
    public static IReadOnlyList<ICodegenHook<TPhase, TContext>> Discover<TPhase, TContext>(
        params Assembly[] assemblies)
        where TPhase : struct, Enum;
}

Supports: hook assembly next to emitter, [CodegenHook] attribute for explicit registration, test injection via BindingProject.RegisterHooks(...).

7.5 Testing hooks without emitters

[Fact]
public void EndDrawing_hook_injects_notify()
{
    var source = """
        namespace Novolis.Raylib.Rendering;
        public static partial class Graphics {
            public static void EndDrawing() => Raylib6Native.EndDrawing();
        }
        """;
    var unit = Parse(source);
    var result = new InjectEndDrawingNotifyHook().Transform(unit, context);
    Assert.Contains("NotifyAfterEndDrawing", result.ToFullString());
}

Library ships CodegenSyntaxAssert helpers (contains/type/method lookup on trees).

7.6 Future Roslyn capabilities (explicitly out of v1 unless needed)

CapabilityUse case
SyntaxFactory emittersReplace string StringBuilder emitters gradually
IIncrementalGeneratorCompile-time binding gen (different product; not replacing maintainer pipeline)
SemanticModelValidate façade bodies reference existing interop methods at codegen time
Roslyn analyzersCI guard: hand-edited *.g.cs detection (already partially via drift)

8. Pipeline integration

8.1 Extract to Novolis.CodeGen.Pipeline

Move from Novolis.Raylib.CodeGen.Abstractions + Novolis.Raylib.Pipeline kernel:

  • IPipelineStep, PipelineContext, StepExecutionResult, StepResultDocument
  • PipelineRunner, StepSkipEvaluator, StepResultWriter, StepFileFingerprint
  • PipelineProfiles pattern (consumer defines profiles)

De-raylib: PipelinePaths becomes injectable IPipelineLayout:

public interface IPipelineLayout
{
    string RepoRoot { get; }
    string StepsRoot { get; }
    string StepDir(string stepId);
    string ManifestDir { get; }
}

Raylib implements RaylibPipelineLayout with codegen/pipeline/raylib6/.

8.2 step_06 wiring

// Before:
var pipeline = new RaylibCodegenPipeline(context.RepoRoot);
pipeline.GenerateBindingsOnly(context.Log);

// After:
RaylibBindingProject.Emit(context.RepoRoot);

Input hashing for step_06: all *.manifest.json in manifest dir or SHA256 of C# fragment source files if JSON is derived-only.


9. Ergonomics checklist

What makes the library comfortable day-to-day:

FeatureBenefit
Template.* constantsNo typos in template strings; rename-safe
.Import("Foo", Template.VoidVoid)Same shape as JSON row, better IDE support
.SuggestFromHeader(path)Replaces suggest-raylib CLI for exploration
.ImportFromJson(path)One-time bootstrap from committed manifests
BindingSurface +Combine Core + GUI without copy-pasting merge blocks
.When(condition, …)Raygui optional path matches IncludeRaygui MSBuild flag
project.EmitDryRun()Print planned outputs + SHA256 without writing
project.DiffAgainstRepo()Pre-commit drift preview (step_07 helper)
Hook unit tests via parsed snippetsFast feedback without full pipeline

10. Migration plan

PhaseWorkRisk
0Document parity (this spec)—
1Extract Novolis.CodeGen.Pipeline + Novolis.CodeGen.Bindings.Roslyn; raylib references packagesLow
2Add fragment records + JSON round-trip tests against all 8 manifest filesLow
3Introduce BindingProject executor wrapping existing emitters unchangedLow
4RaylibBindingProject + switch step_06; parity test: emit diff emptyMedium
5C# manifest authoring in Novolis.Raylib.Manifests; JSON becomes derivedMedium
6Optional: SyntaxFactory emitters for one template groupLow priority

Do not migrate yet: flattening interop classes, changing manifest JSON schema, removing SHA256 headers from *.g.cs.


11. Open questions

  1. Façade authoring in C#: Full method bodies in code vs keep JSON/generated for large façades with enrich step — hybrid (Partial classes per façade type file)?
  2. Semantic validation: Should BindingProject.Build() fail if Gui.Setup body references unknown symbol (needs parse + symbol table of merged interop)?
  3. Package granularity: Ship Novolis.CodeGen.Bindings as one package or split Pipeline / Roslyn for lighter dependents?
  4. Second consumer: Which repo validates generics first — novolis-rendering (Silk) or stay raylib-only until parity tests pass?

12. Summary

  • Today: eight JSON manifests, four emit strategies, three hook phases, one Bindings assembly combining Raylib + ImGui interop, optional Raygui add-on.
  • Library: fragment types + merge points express that graph in C#; JSON derived for agents/drift; pipeline kernel and Roslyn hook host shared via novolis-codegen.
  • Roslyn role: parse → ordered hook rewrites → format; helpers lower the cost of adaptations like doc injection, inlining, and cross-cutting notify injection.
  • Success criterion: RaylibBindingProject.Emit(repoRoot) is byte-identical to current step_06_codegen on a clean tree bootstrapped from existing manifests.