Skip to content

engine: multi-file specs — bounded load-and-bundle and a sources-based entry point #74

Closed

Description

@OmarAlJarrah

Problem

Split-file OpenAPI repos — the common real-world layout — don't work at all:

  • A minimal two-file spec with a correct relative $ref: './child.yaml#/components/schemas/Child' yields error openapi/unresolved-ref and the referencing type degrades to t/prim/any (verified with absolute and relative CWDs).
  • architecture.md §2.1 phase 1 promises "Load & bundle — read all source files, resolve external references"; the harness comment confirms the gap is known ("the compiler does no file I/O, so the external ref surfaces as an unresolved-ref error").
  • DisableExternalRefs exists but does nothing meaningful when false (and its current default separately violates the purity contract — bug openapi: Compile performs filesystem and network I/O by default via external $ref resolution #31).
  • Document.Sources is hard-coded to exactly one entry (compilers/openapi/openapi.go:58).
  • engine.Run(ctx, specPath, opts) does its own os.ReadFile and always passes exactly one compilers.Source, although the compiler contract accepts a slice by design ("the caller loads bytes"). Embedders with in-memory specs must write temp files today.

Proposed direction

Two stages, both sized against the docs' own milestones:

  1. The seam (S, now): add engine.RunSources(ctx, []compilers.Source, RunOptions) and make Run a thin file-loading wrapper. Two functions, no behavior change — the multi-file seam stops being a future breaking change to the engine's only entry point.
  2. Load-and-bundle (L): a bounded file loader (root-relative, no network by default, file-count/size budgets per openapi+engine: resource budgets for pathological-but-legal inputs #75) wired into reference resolution; every contributing file recorded in Document.Sources with its hash; provenance Source indices pointing at the right file. Until it lands, make external-ref behavior honest: unresolved-with-diagnostic by default and DisableExternalRefs not a trap.

Note for the cache work (#76): once multiple files contribute, all of them must appear in Sources for cache keys to stay sound — design together.

Acceptance

  • The two-file repro compiles with the child type resolved, Sources listing both files, and provenance pointing into the correct one.
  • In-memory compilation via RunSources needs no temp files.

Activity

  1. OmarAlJarrah commented on Sep 30, 2026

    @OmarAlJarrah
    MemberAuthor

    Two details for the load-and-bundle work:

    • With allow-external-refs=true, cross-document references to parameters and responses resolve today (the fixtures resolve_main_external_valid.yaml and resolve_main_alias_external_valid.yaml cover them), but a cross-document schema reference never does. A one-hop $ref: './components.yaml#/components/schemas/X' is openapi/unresolved-ref whether the target file is a full OpenAPI document or a components-only fragment. The passing tests therefore don't show that multi-file schemas work.
    • If the implementation reuses the parser's openapi.Bundle, it doesn't rebase discriminator mapping values (they are URI references too), and it doesn't copy a schema that is reachable only through a mapping. Both surface as unresolved-ref on the mapping after bundling.
  2. added
    priority:P2Rarer spec shapes, or an IR decision to settle before the emitter
    priority:P3Later emitters, M4 and up, 3.2-only, streaming, webhooks, rare constructs
    and removed
    priority:P2Rarer spec shapes, or an IR decision to settle before the emitter
    on Oct 19, 2026
  3. OmarAlJarrah commented on Oct 19, 2026

    @OmarAlJarrah
    MemberAuthor

    Two related results at c387e25c:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority:P3Later emitters, M4 and up, 3.2-only, streaming, webhooks, rare constructstype:featureNew capability or enhancement

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions