# schemas/lib/build_spec.ncl — BuildSpec contract (ADR-039) # # Schema for .build-spec.ncl files at the root of each built repo. # buildkit-launcher validates against this schema at parse time and exits # non-zero on failure (constraint: build-spec-schema-versioned). # # Three-tier sizing resolution (launcher, not schema): # 1. Explicit declaration here (highest priority) # 2. P95 historical from orchestrator SurrealDB × 1.2 # 3. Language-default fallback (lowest priority) # # Usage: # let bs = import "schemas/lib/build_spec.ncl" in # { .. } | bs.BuildSpec let positive_number_ = std.contract.custom ( fun label => fun value => if value > 0 then 'Ok value else 'Error { message = "Expected a positive number, got '%{std.to_string value}'.\nAll resource fields must be > 0" } ) in let bounded_cpu_ = std.contract.custom ( fun label => fun value => if value > 0 && value <= 256 then 'Ok value else 'Error { message = "Invalid cpu value '%{std.to_string value}'.\nValid range: (0, 256]" } ) in let bounded_time_budget_ = std.contract.custom ( fun label => fun value => if value > 0 && value <= 1440 then 'Ok value else 'Error { message = "Invalid time_budget_min '%{std.to_string value}'.\nValid range: (0, 1440] — max 24 hours" } ) in let _BuildSpec = { schema_version | Number | doc "Schema version — buildkit-launcher rejects files with unknown versions" | default = 1, cpu | bounded_cpu_ | doc "Virtual CPUs to request for the ephemeral runner VM", memory_gb | positive_number_ | doc "RAM in GiB for the runner VM", disk_gb | positive_number_ | doc "Ephemeral disk in GiB; no persistent storage — all state is destroyed with the VM", time_budget_min | bounded_time_budget_ | doc "Hard wall-clock limit in minutes; VM is killed on expiry", cache_keys | Array String | doc "sccache / BuildKit cache key namespaces to warm for this repo" | default = [], oom_retry | Bool | doc "When true, launcher retries once at next size tier on OOM kill; bounded to 1 retry (constraint: oom-retry-bounded)" | default = true, } in { BuildSpec = _BuildSpec, PositiveNumber = positive_number_, BoundedCpu = bounded_cpu_, make_build_spec | not_exported = fun data => data | _BuildSpec, }