122 lines
4.4 KiB
Text
122 lines
4.4 KiB
Text
# schemas/lib/dag/contracts.ncl — DAG domain type contracts
|
|
#
|
|
# Two distinct DAG layers:
|
|
# 1. Capability layer — ExtensionCapability/ExtensionDependency (extension metadata)
|
|
# 2. Composition layer — WorkspaceComposition (inter-formula ordering)
|
|
# 3. Resolution layer — ResolutionPolicy (capability → extension mapping)
|
|
#
|
|
# Pattern: separate let bindings with _ prefix, same as formula.ncl.
|
|
# No self-references, no let rec — each binding is in scope for subsequent ones.
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Capability layer
|
|
# ---------------------------------------------------------------------------
|
|
let _capability_kind = [| 'Required, 'Optional, 'ConflictsWith |] in
|
|
|
|
let _ExtensionCapability = {
|
|
id | String,
|
|
version | String,
|
|
interface | String,
|
|
} in
|
|
|
|
let _ExtensionDependency = {
|
|
capability | String,
|
|
kind | _capability_kind,
|
|
min_version | String | optional,
|
|
} in
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Composition layer — inter-formula DAG
|
|
# Distinct from the intra-formula DAG in formula.ncl (per-server task ordering).
|
|
# WorkspaceComposition declares execution ordering between formulas.
|
|
# ---------------------------------------------------------------------------
|
|
let _composition_condition = [| 'Completed, 'Healthy, 'Running |] in
|
|
|
|
let _FormulaDep = {
|
|
formula_id | String,
|
|
condition | _composition_condition,
|
|
} in
|
|
|
|
let _HealthGate = {
|
|
check_cmd | String,
|
|
expect | String,
|
|
timeout_ms | Number,
|
|
retries | Number,
|
|
check_server | String | optional,
|
|
} in
|
|
|
|
let _FormulaCompositionEntry = {
|
|
formula_id | String,
|
|
depends_on | Array _FormulaDep | default = [],
|
|
parallel | Bool | default = false,
|
|
health_gate | _HealthGate | optional,
|
|
} in
|
|
|
|
# Base shape — used as first step inside the custom contract (same pattern as _FormulaBase
|
|
# in formula.ncl) so missing-field errors surface before cross-field validation runs.
|
|
let _WorkspaceCompositionBase = {
|
|
formulas | Array _FormulaCompositionEntry,
|
|
} in
|
|
|
|
# Custom contract: validates referential integrity across formula entries.
|
|
# - At least one formula must have depends_on = [] (root node)
|
|
# - All depends_on[].formula_id must reference a declared formula_id
|
|
let _WorkspaceComposition = std.contract.custom (fun label value =>
|
|
let base = value | _WorkspaceCompositionBase in
|
|
let ids = base.formulas |> std.array.map (fun e => e.formula_id) in
|
|
let has_root = base.formulas |> std.array.any (fun e => e.depends_on == []) in
|
|
|
|
let bad_deps = base.formulas |> std.array.flat_map (fun e =>
|
|
e.depends_on
|
|
|> std.array.filter (fun d =>
|
|
!(ids |> std.array.any (fun id => id == d.formula_id))
|
|
)
|
|
|> std.array.map (fun d =>
|
|
"formula '%{e.formula_id}' depends_on unknown '%{d.formula_id}'"
|
|
)
|
|
) in
|
|
|
|
if !has_root then
|
|
std.contract.blame_with_message
|
|
"WorkspaceComposition: at least one formula must have depends_on = []"
|
|
label
|
|
else if (std.array.length bad_deps) > 0 then
|
|
std.contract.blame_with_message
|
|
"WorkspaceComposition: invalid depends_on references: %{std.string.join ", " bad_deps}"
|
|
label
|
|
else
|
|
'Ok base
|
|
) in
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Resolution layer — capability → concrete extension mapping
|
|
# ---------------------------------------------------------------------------
|
|
let _resolution_strategy = [| 'Strict, 'BestEffort |] in
|
|
|
|
let _ResolutionEntry = {
|
|
capability_id | String,
|
|
extension_name | String,
|
|
} in
|
|
|
|
let _ResolutionPolicy = {
|
|
strategy | _resolution_strategy,
|
|
overrides | Array _ResolutionEntry | default = [],
|
|
allow_optional_gaps | Bool,
|
|
} in
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Exports
|
|
# ---------------------------------------------------------------------------
|
|
{
|
|
CapabilityKind = _capability_kind,
|
|
ExtensionCapability = _ExtensionCapability,
|
|
ExtensionDependency = _ExtensionDependency,
|
|
CompositionCondition = _composition_condition,
|
|
FormulaDep = _FormulaDep,
|
|
HealthGate = _HealthGate,
|
|
FormulaCompositionEntry = _FormulaCompositionEntry,
|
|
WorkspaceComposition = _WorkspaceComposition,
|
|
ResolutionStrategy = _resolution_strategy,
|
|
ResolutionEntry = _ResolutionEntry,
|
|
ResolutionPolicy = _ResolutionPolicy,
|
|
}
|