Dk0.Closure, Dk1.Closure, and TestPkg.Closure (plus the final-exe pair-files Dk0.values.jsonc/Dk1.values.jsonc) are GENERATED from the opam lock that Dk0.MlFrontSource carries (mlfront/dk.opam-lock.jsonc, and the with-test dk-opam-lock-test.jsonc). Each driver stamps its full parameter set into a top-level generated member.
Each driver invokes CommonsLang_OCaml.Dk.OpamBuild.F_BuildLockedClosure: one run-function precommand reads the lock once and registers every per-package form (CommonsBase_Dk.Dk0.Pkg.<Pkg>@<VER>) plus the aggregate .Built output, and the merged prefix is archived deterministically with the in-process --zip INSIDE the rule (so the form shows a cp built/prefix.zip line). TestPkg.Closure builds MlFront's --with-test external transitive dependencies (astring, tezt, crowbar, sedlex, ...) as one merged-prefix object so the gate imports it and skips their cold build; it reuses Dk0's packages via impdep_<Pkg>=CommonsBase_Dk.Dk0.Pkg.<Pkg>@<VER> (skiplocal=t/mergedprefix=t/implib[]=CommonsBase_Dk.Dk0.Pkg), so those ids MUST match what Dk0.Closure exposes.
First-time generation (a brand-new driver, no stamp to adopt) uses GenerateDriver directly: pkgpath=CommonsBase_Dk.Dk0 for Dk0 (the generator appends .Pkg.<Pkg>); Dk1 differs only in out/formid/pkgpath/roots[]=DkOne_Exec; TestPkg adds skiplocal=t mergedprefix=t implib[]=CommonsBase_Dk.Dk0.Pkg impver[]=<new> impsrclock[]=dk.opam-lock.jsonc and roots[] = the gate's TestDeps.
For an existing driver, regeneration is a Refresh that reads the stamps back: Refresh takes the rule family and pkgpath from the stamp. Regenerating with a newer GenerateDriver version is a module change and needs the maintainer's version decision first: the generator's version is part of the drivers' behavior, including the emitted build mechanism and the per-package object namespace.
mode=check compares each driver's stamped rule versions against the pinned CommonsLang_OCaml import and fails with the fix command when one is stale; it runs in distribute-0.1.yml before the build:
dk0 dialog CommonsLang_OCaml.Dk.OpamLock.Refresh@1.1.16 mode=check drivers[]=etc/dk/v/CommonsBase_Dk/Dk0.Closure.values.jsonc drivers[]=etc/dk/v/CommonsBase_Dk/Dk1.Closure.values.jsonc drivers[]=etc/dk/v/CommonsBase_Dk/TestPkg.Closure.values.jsonc
mode=check does not catch an impdep id mismatch; the CI TestPkg build fails on one with [917b6e5f] ... does not exist.
Lock content change (a source repin, i.e. every release): materialize the lock from its source object, then Refresh with version=<new> and the lock on disk. Build Dk0.MlFrontSource@<new> (get-object ... -d out) for dk-opam-lock.jsonc; the with-test dk-opam-lock-test.jsonc is inside that output.zip at mlfront/dk.opam-lock-test.jsonc. Refresh reads each driver's OWN stamped lock name, so run the two lock families separately (a single lock= overrides every driver, which is wrong for the mixed set), and stage the dotted dk.opam-lock.jsonc that TestPkg's impsrclock names:
# Dk0/Dk1 (+ their pair-files): the non-test lock. dk0 dialog CommonsLang_OCaml.Dk.OpamLock.Refresh@<DRIVER_VER> drivers[]=etc/dk/v/CommonsBase_Dk/Dk0.Closure.values.jsonc drivers[]=etc/dk/v/CommonsBase_Dk/Dk1.Closure.values.jsonc version=2.4.3 lock=dk-opam-lock.jsonc
# TestPkg (with-test merged prefix): the test lock. cp # mlfront/dk.opam-lock.jsonc dk.opam-lock.jsonc first so the impsrclock # resolves: dk0 dialog CommonsLang_OCaml.Dk.OpamLock.Refresh@<DRIVER_VER> driver=etc/dk/v/CommonsBase_Dk/TestPkg.Closure.values.jsonc version=2.4.3 lock=dk-opam-lock-test.jsonc
Rule version-only bump (same generator, newer rulefn): drop mode=check. Refresh rewrites in place from the stamp, with no lock on disk needed and no reformatting drift.
dist/Linux_x86_64.u, dist/Windows_x86_64.u, dist/Darwin_arm64.u and dist/Darwin_x86_64.u get-object the TestPkg object so it is built and published for the gate to import. The 32-bit cross slots dist/Linux_x86.u and dist/Windows_x86.u do not self-test it yet.