100%

Walking Skeleton

Goal

Prove the thinnest real end-to-end seam: ori_codegen owns the pure CodegenBackend/input/artifact contract, a single-variant CodegenBackendChoice::Llvm enum in oric wraps the existing LLVM compile and finalization path with no behavior change, and both compile_common.rs entry points route through the one dispatch module. Build the backend_stub_conformance integration test fixture this plan’s north-star gate probes.

Context

Research (compiler_repo/compiler/oric/src/commands/compile_common.rs) confirmed exactly two production entry points (compile_to_llvm_with_imported_monos, compile_to_llvm_with_imports) both terminating in codegen_pipeline::run_codegen_pipeline. This section proves the final dependency direction and typed input/artifact shape while delegating to the shipped LLVM implementation before any deeper migration.

Implementation sketch

  1. Create the narrow ori_codegen crate. Define immutable CodegenInput<'a> { program: &'a ExecutableProgram, target: &'a TargetSpec, layout: &'a CompiledLayoutPlan }, typed ArtifactRequest, backend-neutral EmittedArtifact, and BackendError. Construction validates program/target/layout fingerprints before a backend is entered.
  2. Define CodegenBackend with a backend-private associated Module, compile(&CodegenInput), and finalize(Module, &ArtifactRequest) -> EmittedArtifact. The associated module is visible only inside a generic compile_and_finalize<B: CodegenBackend> helper; no backend-private IR reaches oric orchestration.
  3. Define enum CodegenBackendChoice { Llvm } in oric and one exhaustive dispatch module. The LLVM implementation delegates compilation to the existing run_codegen_pipeline and delegates finalization to the existing LLVM object/WASM emission path.
  4. Route both compile_common.rs entry points through CodegenBackendChoice::Llvm and the generic compile/finalize helper. Linking and execution consume only the returned typed native-object or complete-WASM artifact.
  5. Create a backend_stub_conformance Cargo feature and integration-test backend implementing the contract with a private dummy module and deterministic emitted artifact. Add negative pins for mismatched target/layout fingerprints and an artifact request the stub does not support.
  6. Verify ori build and ori test --backend=llvm preserve shipped LLVM IR, native object/WASM bytes where deterministic, linking, diagnostics, and observable behavior.

Narrowed contract and deferral anchors

Source-verified at HEAD: TargetSpec and CompiledLayoutPlan do not exist as types anywhere in compiler_repo/compiler/ (doc-comment prose only), and ExecutableProgram is constructed inside run_codegen_pipeline (codegen_pipeline/pipeline.rs), downstream of the dispatch boundary. This section therefore ships ori_codegen::CodegenInput over the inputs the seam carries today and defers the richer contract to its named owners:

  • TargetSpec + CompiledLayoutPlan fields on CodegenInput — owner: plans/repr-opt/section-13-multi-backend-layouts.md (declares CompiledLayoutPlan/VmLayoutPlan over shared immutable evidence) and this plan’s “ in codegen-backend-trait-and-dispatch--s-90024726.md (which owns ori_target and fleshes out CodegenInput). Inventing either type here would fork repr-opt §13’s SSOT, and naming today’s ReprPlan as CompiledLayoutPlan would misname a transitional carrier.
  • ExecutableProgram as the CodegenInput payload — owner: “ in shared-pre-codegen-orchestration--s-30e3c9a6.md, which routes the driver through realize_closed_program() before dispatch. Hoisting it here would also relocate the live register_resolved_collection_burdens typeck call the walking skeleton must route around, not through.
  • finalize() / ArtifactRequest / EmittedArtifact and the mismatched target/layout + unsupported-artifact negative pins — owner: “, which names all five verbatim.

Shipped in this section instead: the ori_codegen crate owning the moved contract, the CodegenBackendChoice rename, and a negative pin that is expressible today (a refusing stub backend proving BackendError crosses the closed-enum dispatch intact).

Spec references

.claude/rules/canon.md §1 (pipeline phase 8, sub-layer 7a ori_repr); .claude/rules/compiler.md §Dispatch (enum for fixed sets).

Work items

  • Define the pure ori_codegen contract over ExecutableProgram + explicit TargetSpec + matching CompiledLayoutPlan; add private-module compile plus typed artifact finalization; add single-variant CodegenBackendChoice dispatch in oric; route both compile_common.rs entry points through it; and build the positive/negative backend_stub_conformance fixture.

Historical Intel Snapshot (Superseded by the Current Contract)

The folded snapshot below predates ExecutableProgram, TargetSpec, CompiledLayoutPlan, ori_codegen, and the compile/finalize artifact boundary. Its RealizedProgram, ori_repr::CodegenBackend, and BackendChoice prescriptions are research history only; the normative goal, sketch, and work item above replace them.

The walking-skeleton is a genuinely thin, low-risk stub — but the graph surfaces two facts the section body does not state that change how narrow “thin” can actually be:

  1. The trait boundary cannot be a pure realized-IR-in/artifact-out contract. run_codegen_pipeline — the exact function the new LlvmBackend::compile() wraps unchanged — reaches directly into ori_types::infer::expr::calls::monomorphization::burden::register_resolved_collection_burdens (§3.1, §3.4 surprise edge 2.64, corroborated by callees list). Codegen is not typeck-independent today. RealizedProgram (defined in THIS section) either bundles enough typeck-registry state to satisfy that call, or the call moves before the trait boundary in §02 (shared-pre-codegen-orchestration). Either way, the walking-skeleton’s RealizedProgram shape is a real design decision, not a rename exercise — [JOIN] typeck-into-codegen coupling + the plan’s own step 2 (“no field changes yet — §02 does the real orchestration unification”).
  2. The graph cannot see the two call sites this section routes through BackendChoice. run_codegen_pipeline shows ZERO callers in the CALLS graph (§3.1, §3.3) despite two confirmed real call sites in compile_common.rs (verified by direct source read). This is a super::-qualified, multi-line-argument call the tree-sitter/SCIP extractor fails to resolve; the extractor gap is only partially closed (§9). Do not trust graph-only “0 callers” as evidence of dead code anywhere in this crate; verify by source read.
  3. The MISSING_ABSTRACTION signal directly validates the fix. compile_to_llvm_with_imported_monos ~ compile_to_llvm_with_imports scores 0.933 similarity (§5) — the graph independently flags the exact pair this section’s trait boundary collapses. run_codegen_pipeline and the JIT evaluator’s compile_all_functions sit in the SAME community (900, size 203; §3.6) — corroborating the mission’s “Known bugs folded into scope” LEAK:algorithmic-duplication (SSOT-18), which §02 (not this section) fixes.
  4. The scope-decision boundary is empirically large and well-drawn. ori_llvm::aot:: carries 943 symbols reached directly from ~15+ oric::commands::build::* call sites (linking, mangling, object emission, incremental cache; §3.3). The mission’s decision to keep AOT/link machinery OUT of CodegenBackend::compile() is not a convenience cut — it is the only way to keep this section’s stub genuinely thin. Do not let compile()’s signature grow to reach any of these 943 symbols.
  5. run_codegen_pipeline itself is pub(super) (source-verified), explicitly commented “private pipeline helper — params match the data flow from both public compile functions.” LlvmBackend’s impl must live where pub(super) visibility resolves (inside oric::commands or a submodule) — a detail the section’s sketch does not spell out but the visibility constraint forces.

Historical decisions from that snapshot, retained only as evidence:

  1. The typeck-to-codegen burden-registration edge had to move before the closed artifact boundary; the current ExecutableProgram contract owns that resolution.
  2. Verify the two compile_common.rs -> run_codegen_pipeline call sites BY SOURCE READ, not by graph query — the graph shows zero callers for this specific symbol (finding 2).
  3. Keep platform linking, sysroot discovery, packaging, and process launch outside the backend interface; backend-local object/module emission is now deliberately covered by typed finalize().
  4. Place the LLVM adapter where it can reach the private pipeline helper while implementing the ori_codegen contract without widening helper visibility.

DIG DEEPER

scripts/intel-query.sh dag-ascii backend-boundary --human
scripts/intel-query.sh plan-status backend-boundary --human

(full dossier: walking-skeleton—s-fdca8921.intel.md)