diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,168 @@
 
 ---
 
+## [0.17.6] - 2026-08-30
+
+Point release on the 0.17.x line, and the last backward-incompatible batch before the 1.0 surface
+freeze. Five kernel-surface changes land together as the "0.17 breaking batch":
+`null` becomes `isEmpty`, `read*`/`show*` become `parse*`/`print*`, `TypeScheme.constraints` and
+`TypeVariableConstraints.classes` change shape, and the constructor primitives are finalized.
+Alongside them, a genuine soundness gap in type inference is fixed — class constraints now propagate
+through function-domain positions — and the cold-seed path is rebuilt on the Java host so that
+breaking core-type changes are no longer structurally impossible to bootstrap.
+
+**Backward-incompatible.** All five surface changes below alter the published kernel. Code written
+against 0.17.5 must be updated:
+
+- **[#682]** `hydra.lib.{lists,sets,maps,strings}.null` -> `.isEmpty`.
+- **[#691]** the `read*`/`show*` family -> `parse*`/`print*`.
+- **[#683]** `TypeScheme.constraints`: `optional<map>` -> plain `map`.
+- **[#685]** `TypeVariableConstraints.classes`: `list` -> `set`.
+- **[#687]** constructor primitives finalized: `lists.pure` is removed; `optionals.given`,
+  `eithers.left`/`right` and `pairs.pair` are added.
+
+[#682]: https://github.com/CategoricalData/hydra/issues/682
+[#683]: https://github.com/CategoricalData/hydra/issues/683
+[#685]: https://github.com/CategoricalData/hydra/issues/685
+[#687]: https://github.com/CategoricalData/hydra/issues/687
+[#691]: https://github.com/CategoricalData/hydra/issues/691
+
+### Highlights
+
+- **Class constraints propagate through contravariant positions**
+  ([#702](https://github.com/CategoricalData/hydra/issues/702)): inference silently dropped a class
+  constraint when the constrained type variable was bound through the **domain** (input) position of a
+  higher-order function argument, while transferring it correctly in output positions — a real soundness
+  gap in the type checker. It had been papered over twice by freezing `encodeMap`/`encodeSet` to
+  monomorphic `Int`. Both freezes and the [#554](https://github.com/CategoricalData/hydra/issues/554)
+  `toPrimitive` stopgap are now removed, the definitions re-polymorphized, and a domain-position
+  regression test added. A `sets.filter` overlay lands across Java, Python, Scala, TypeScript and the
+  Lisp dialects as its first consumer.
+- **Cold-seed rebuilt on the Java host**
+  ([#703](https://github.com/CategoricalData/hydra/issues/703)): CI's cold-seed steps compiled HEAD's
+  kernel `Types` source against the *published* Hackage `hydra-kernel`, which is sound only for additive
+  changes and structurally impossible for any breaking core-type-shape change — the root of a recurring
+  red-CI class (#500, #608, #617, #417, #683). The Haskell cold-seeder (`ColdSeedMain`, `typesmods/`,
+  Check-2) is deleted and replaced by a data-driven Java-host schema-walking seed
+  (`seed-dist-haskell.sh`), which decodes `dist/json` and emits Haskell text without ever linking
+  HEAD types against a published kernel.
+- **`hydra.paths` completed** ([#716](https://github.com/CategoricalData/hydra/issues/716)):
+  the step unions are realigned with the `hydra.core` term and type grammars — missing variants
+  added (`annotatedAnnotation`, `eitherLeft`/`eitherRight`, `effect`), undirected steps given a
+  direction, and variants named after retired constructors renamed. `hydra.print.paths` is rewritten
+  as serialization-only (printers, parsers and a round-trip law), and the graph view moves to a new
+  `hydra.shredding` module (`shredGraph`/`shredTerm`/`shredSchema`), which the Graphviz coder now
+  consumes.
+- **`hydra.lib.ordering` is genuinely structural on every host**
+  ([#718](https://github.com/CategoricalData/hydra/issues/718)): the Java, Python and Scala overlay
+  implementations fell back to comparing *printed strings* for non-literal and mixed-variant terms,
+  contradicting the normative `ordering-and-equality.md` (records field-by-field in declaration
+  order, unions by declared-variant order then payload, wrappers by wrapped value, decimals by
+  numeric value then scale — with no print-based fallback anywhere). All three are now structural;
+  Java delegates to the generated `compareTo`.
+- **Bash driver-layer audit** ([#714](https://github.com/CategoricalData/hydra/issues/714)):
+  a prerequisite to the remaining #416 promotion. Six dead build scripts superseded by
+  `transform-json-to-target.sh` are deleted, and the #608 oil-and-water and #540 header-idempotency
+  regression harnesses are wired into `test-regressions.sh`.
+
+- **Backward-compatibility and deprecation policy**
+  ([#676](https://github.com/CategoricalData/hydra/issues/676)): adds the normative spec chapter defining
+  what "compatible" means after 1.0 — the additivity calculus, the deprecation/rename mechanism, and
+  enforcement. `LifecycleInfo.deprecatedSince` moves from defined-but-unused to populated on **32**
+  kernel primitives slated for 0.18 deprecation, via a new `deprecatedSince` DSL helper.
+- **Effect-sequencing contract specified and tested**
+  ([#675](https://github.com/CategoricalData/hydra/issues/675)): `hydra.lib.effects` carries a sequencing
+  contract — effects composed in a specified order, exactly once each, with `bind`'s continuation deferred
+  until the prior effect has run. On the lazy Haskell host this falls out of the evaluation model; on the
+  eight eager hosts it depended entirely on each hand-written interpreter. The obligations are now written
+  into `effects.md` and backed by ordering-observation tests for `bind`/`foldList`/`mapList`/`mapOptional`.
+- **Non-GC memory-discipline investigation**
+  ([#678](https://github.com/CategoricalData/hydra/issues/678)): Hydra's data model says nothing about
+  allocation, freeing, or ownership, which is what gates Swift, Rust, C++ and C as runtime hosts. This
+  investigation delivers per-language verdicts plus leak-free proof-of-concept implementations — Rust
+  (`Box` structural ownership), C++ (`unique_ptr` + arena) and C (arena), all ASan/LSan-clean on
+  construct-and-drop of a recursive `Term` — and an airtight analysis of Swift's class-forcing requirement.
+- **Build system promotion continues** ([#416](https://github.com/CategoricalData/hydra/issues/416)):
+  adds `hydra.build.Registry`, which models per-language build identity as data (name / coder package /
+  family) with per-script scope lists. `syncMatrix` and `langExpansion` are rewired onto it, and the
+  hardcoded language lists in the test and bench scripts are retired in favour of a generated
+  `languages.json`. Later in the cycle: `publishsets.reverseDepClosure` and
+  `publishsets.requiresCoders` promote reverse-dependency propagation and per-package coder-loading
+  into data, an `expected-libraries.json` artifact lets the Common Lisp and Emacs Lisp harnesses
+  fail fast on a missing `hydra.lib`, and a host-independent `apply-assembly-plan.sh` executor
+  (with a conformance test) takes over the Java `assemble-distribution` Step 0.
+- **Serialization specification chapter**
+  ([#674](https://github.com/CategoricalData/hydra/issues/674)): documents serializability, the
+  encode/decode contract, and the round-trip law; `isSerializable` now forbids `void`
+  ([#690](https://github.com/CategoricalData/hydra/issues/690)), and `hydra.lib.functions.absurd` is added
+  as the void eliminator ([#684](https://github.com/CategoricalData/hydra/issues/684)).
+
+### Bug fixes
+
+- **Hoisting silently produced terms with free variables**
+  ([#717](https://github.com/CategoricalData/hydra/issues/717)):
+  `augmentBindingsWithNewFreeVars` returned a binding *unchanged* when a captured variable had no
+  type in the context, yielding a top-level binding with free variables — a term valid before
+  hoisting and invalid after, produced with no error. It is now total and fails loudly, per the
+  kernel's fail-immediately rule.
+- **TypeScript `div`/`mod` used truncating semantics**
+  ([#677](https://github.com/CategoricalData/hydra/issues/677)): corrected to floor/Knuth division via
+  shared bigint helpers, with an `Optional` codomain; the dead `maybeDiv`/`maybeMod` are deleted and an
+  emitted-path test added.
+- **Overlay lag behind the `null` -> `isEmpty` rename**
+  ([#682](https://github.com/CategoricalData/hydra/issues/682)): the Java, Python, Scala, TypeScript and
+  Lisp overlays each kept a stale spelling (`isEmpty_`, a half-renamed Python `null` impl, and Lisp
+  camelCase where the coder emits snake_case), breaking generation until reconciled.
+- **`check-haskell-def-completeness` could not resolve qualified assembly references**
+  ([#683](https://github.com/CategoricalData/hydra/issues/683)), surfaced once the #703 cold-clone path
+  began reaching the check.
+- **Void-typed definitions leaked into DSL-ref generation**
+  ([#684](https://github.com/CategoricalData/hydra/issues/684)): `absurd` and friends are now excluded
+  across all hosts.
+- **Scheme bootstrap loader gaps** ([#641](https://github.com/CategoricalData/hydra/issues/641)):
+  missing `functions.scm`/`ordering.scm` in the native-lib load list, missing defaults in the def-module
+  load list, and a guile-only import allowlist.
+- **Bootstrap diff detail was swallowed** ([#671](https://github.com/CategoricalData/hydra/issues/671)):
+  now persisted to `<run>/<path>.diff`, with `compare_output`'s return value no longer discarded.
+  Separately, `compare_output` mis-derived the `src/main` vs `src/test` root per file
+  ([#706](https://github.com/CategoricalData/hydra/issues/706)).
+- **Nested duplicate output path** ([#664](https://github.com/CategoricalData/hydra/issues/664)) from
+  `hydra-kernel` being hardcoded into `defaultOutput`.
+- **Bootstrap codegen exceptions hid their stack traces**
+  ([#707](https://github.com/CategoricalData/hydra/issues/707)) in `bootstrap.ts`.
+
+### Release tooling
+
+- `bin/check-release-env.sh` ([#704](https://github.com/CategoricalData/hydra/issues/704)) adds a
+  publish-time toolchain preflight, after JDK- and twine-version problems cost real time at 0.17.5.
+- The TypeScript head's node floor is lowered to `^18.0.0 || >=20.0.0`, the true vitest constraint
+  ([#708](https://github.com/CategoricalData/hydra/issues/708)).
+- Hydra does not create GitHub Releases; the detached `.asc` signature stays in `release-artifacts/`
+  until [#441](https://github.com/CategoricalData/hydra/issues/441)
+  ([#508](https://github.com/CategoricalData/hydra/issues/508)).
+
+### Known issues
+
+- The `hostOverrides` shim is pinned to `{java: local, python: local}` for the duration of the breaking
+  batch, since the new kernel surface is by definition absent from published 0.17.5. It must be retired
+  once 0.17.6 is visible in Maven Central and PyPI
+  ([#508](https://github.com/CategoricalData/hydra/issues/508)).
+- **`hydra-ext` does not ship to Maven Central for Java**, as in 0.17.4 and 0.17.5: the Java coder's
+  visitor-pattern inner interface still collides with an enclosing type named `Visitor`
+  ([#643](https://github.com/CategoricalData/hydra/issues/643)), so the package does not compile for
+  that target. It publishes to Hackage and PyPI as usual.
+- The breaking batch leaves three deliberate 0.18 tails, each separately tracked:
+  `effects.pure` is retained provisionally pending a name decision
+  ([#688](https://github.com/CategoricalData/hydra/issues/688)); the `printable` type class that would
+  promote the print/parse convention into enforced dispatch awaits
+  [#497](https://github.com/CategoricalData/hydra/issues/497); and the literal conversion lattice is
+  gated on [#129](https://github.com/CategoricalData/hydra/issues/129) via
+  [#689](https://github.com/CategoricalData/hydra/issues/689). Anticipated `TypeClassConstraint`
+  variants remain open as [#686](https://github.com/CategoricalData/hydra/issues/686) — the `set`
+  element type must stay orderable as variants are added.
+
+---
+
 ## [0.17.5] - 2026-08-19
 
 Point release on the 0.17.x line. Two language-surface changes lead: sum-type eliminators unify on
diff --git a/hydra-build.cabal b/hydra-build.cabal
--- a/hydra-build.cabal
+++ b/hydra-build.cabal
@@ -1,11 +1,11 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.38.1.
+-- This file has been generated from package.yaml by hpack version 0.39.1.
 --
 -- see: https://github.com/sol/hpack
 
 name:           hydra-build
-version:        0.17.5
+version:        0.17.6
 synopsis:       Hydra's build system, promoted into Hydra
 description:    Hydra is an implementation of the LambdaGraph data model, which takes advantage of an isomorphism between labeled hypergraphs and typed lambda calculus: in Hydra, "graphs are programs, and programs are graphs". This package is Hydra's build system, promoted into Hydra itself: the manifest-derived module-to-package router, kernel/host reconciliation utilities, and pure module-list helpers, expressed as Hydra modules and generated into every self-hosting dialect. It builds on hydra-kernel.
 category:       Data
@@ -40,6 +40,7 @@
       Hydra.Build.PackagingProfile
       Hydra.Build.Publishsets
       Hydra.Build.Reconcile
+      Hydra.Build.Registry
       Hydra.Build.Routing
       Hydra.Build.Syncmatrix
       Hydra.Build.Versionconsistency
@@ -63,6 +64,6 @@
   build-depends:
       base >=4.19.0 && <4.22
     , containers >=0.6.7 && <0.8
-    , hydra-kernel ==0.17.5
+    , hydra-kernel ==0.17.6
     , scientific >=0.3.7 && <0.4
   default-language: Haskell2010
diff --git a/src/main/haskell/Hydra/Build/Assembly.hs b/src/main/haskell/Hydra/Build/Assembly.hs
--- a/src/main/haskell/Hydra/Build/Assembly.hs
+++ b/src/main/haskell/Hydra/Build/Assembly.hs
@@ -7,6 +7,7 @@
 import qualified Hydra.Core as Core
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | A single copy-or-link operation in an assembly plan: realize a source tree at a destination tree by the given kind, optionally excluding matching relative paths.
 data AssemblyEntry =
diff --git a/src/main/haskell/Hydra/Build/Assemblyplan.hs b/src/main/haskell/Hydra/Build/Assemblyplan.hs
--- a/src/main/haskell/Hydra/Build/Assemblyplan.hs
+++ b/src/main/haskell/Hydra/Build/Assemblyplan.hs
@@ -38,6 +38,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | The full assembly plan (entries, keepPaths) for a (package, language) pair
 deriveAssemblyPlan :: String -> String -> [String] -> [(String, t0)] -> ([(String, (String, String))], [(String, t0)])
diff --git a/src/main/haskell/Hydra/Build/Benchresult.hs b/src/main/haskell/Hydra/Build/Benchresult.hs
--- a/src/main/haskell/Hydra/Build/Benchresult.hs
+++ b/src/main/haskell/Hydra/Build/Benchresult.hs
@@ -7,6 +7,7 @@
 import qualified Hydra.Core as Core
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Map as M
 
 -- | Per-run metadata: timestamp and git context of a benchmark run
diff --git a/src/main/haskell/Hydra/Build/Comparereport.hs b/src/main/haskell/Hydra/Build/Comparereport.hs
--- a/src/main/haskell/Hydra/Build/Comparereport.hs
+++ b/src/main/haskell/Hydra/Build/Comparereport.hs
@@ -7,6 +7,7 @@
 import qualified Hydra.Core as Core
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | The whole compare-report: per-module records plus the byte-identical tally
 data CompareReport =
diff --git a/src/main/haskell/Hydra/Build/Comparereportlogic.hs b/src/main/haskell/Hydra/Build/Comparereportlogic.hs
--- a/src/main/haskell/Hydra/Build/Comparereportlogic.hs
+++ b/src/main/haskell/Hydra/Build/Comparereportlogic.hs
@@ -45,6 +45,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | The whole compare-report (rows, byteEqCount, total) over enumerated module triples
 buildReport :: [(t0, (Maybe String, (Maybe String)))] -> ([(t0, (String, Int))], (Int, Int))
@@ -84,9 +85,9 @@
           rowLines = Lists.map (\r -> renderRow r) rows
           summary =
                   Strings.concat [
-                    Literals.showInt32 byteEqCount,
+                    Literals.printInt32 byteEqCount,
                     "/",
-                    (Literals.showInt32 total),
+                    (Literals.printInt32 total),
                     " byte-identical"]
       in (Strings.join "\n" (Lists.concat [
         rowLines,
@@ -105,7 +106,7 @@
         ": ",
         status,
         " (",
-        (Literals.showInt32 diffLines),
+        (Literals.printInt32 diffLines),
         " lines differ)"]) (Strings.concat [
         name,
         ": ",
diff --git a/src/main/haskell/Hydra/Build/Format.hs b/src/main/haskell/Hydra/Build/Format.hs
--- a/src/main/haskell/Hydra/Build/Format.hs
+++ b/src/main/haskell/Hydra/Build/Format.hs
@@ -8,6 +8,7 @@
 import qualified Hydra.Packaging as Packaging
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Map as M
 
 -- | Configuration for the definition-name-convention check
diff --git a/src/main/haskell/Hydra/Build/Langexpansion.hs b/src/main/haskell/Hydra/Build/Langexpansion.hs
--- a/src/main/haskell/Hydra/Build/Langexpansion.hs
+++ b/src/main/haskell/Hydra/Build/Langexpansion.hs
@@ -5,6 +5,7 @@
 module Hydra.Build.Langexpansion where
 
 import qualified Hydra.Ast as Ast
+import qualified Hydra.Build.Registry as Registry
 import qualified Hydra.Coders as Coders
 import qualified Hydra.Core as Core
 import qualified Hydra.Docs as Docs
@@ -39,21 +40,11 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
--- | The canonical set of all target languages
+-- | The canonical set of all target languages, read from the registry (the language names, in registry order)
 allLanguages :: [String]
-allLanguages =
-    [
-      "haskell",
-      "java",
-      "python",
-      "scala",
-      "go",
-      "typescript",
-      "clojure",
-      "scheme",
-      "common-lisp",
-      "emacs-lisp"]
+allLanguages = Registry.allLanguageNames
 
 -- | Expand a single language token: 'all' -> all languages, 'lisp' -> the dialects, else itself
 expandLangAlias :: String -> [String]
@@ -76,14 +67,9 @@
       hosts,
       targets])
 
--- | The four Lisp dialects the 'lisp' alias expands to
+-- | The Lisp dialects the 'lisp' alias expands to, read from the registry (the family=lisp language names)
 lispDialects :: [String]
-lispDialects =
-    [
-      "clojure",
-      "common-lisp",
-      "emacs-lisp",
-      "scheme"]
+lispDialects = Registry.lispDialectNames
 
 -- | Validate a token list; return the unknown expanded tokens (empty = all valid)
 validateLangs :: [String] -> [String]
diff --git a/src/main/haskell/Hydra/Build/Libraries.hs b/src/main/haskell/Hydra/Build/Libraries.hs
--- a/src/main/haskell/Hydra/Build/Libraries.hs
+++ b/src/main/haskell/Hydra/Build/Libraries.hs
@@ -36,6 +36,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Every hydra.lib.<sub> module name every self-hosting host must register
 expectedLibraries :: [String]
diff --git a/src/main/haskell/Hydra/Build/ManifestWriter.hs b/src/main/haskell/Hydra/Build/ManifestWriter.hs
--- a/src/main/haskell/Hydra/Build/ManifestWriter.hs
+++ b/src/main/haskell/Hydra/Build/ManifestWriter.hs
@@ -37,6 +37,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | The manifest.json contents for one package: field assembly + serialization, given the package name and its main/dsl/encoding/test module lists
 packageManifestJson :: String -> [Packaging.Module] -> [Packaging.Module] -> [Packaging.Module] -> [Packaging.Module] -> Model.Value
diff --git a/src/main/haskell/Hydra/Build/Modules.hs b/src/main/haskell/Hydra/Build/Modules.hs
--- a/src/main/haskell/Hydra/Build/Modules.hs
+++ b/src/main/haskell/Hydra/Build/Modules.hs
@@ -42,6 +42,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Deduplicate a list of strings, preserving first-occurrence order
 dedupPreservingOrder :: Eq t0 => ([t0] -> [t0])
@@ -54,7 +55,7 @@
       let startsWith =
               \prefix -> \s ->
                 let parts = Strings.splitOn prefix s
-                in (Optionals.match (Lists.uncons parts) False (\uc -> Logic.and (Strings.null (Pairs.first uc)) (Logic.not (Lists.null (Pairs.second uc)))))
+                in (Optionals.match (Lists.uncons parts) False (\uc -> Logic.and (Strings.isEmpty (Pairs.first uc)) (Logic.not (Lists.isEmpty (Pairs.second uc)))))
       in (Lists.filter (\m ->
         let name = Packaging.unModuleName (Packaging.moduleName m)
         in (Logic.and (Logic.not (startsWith "hydra." name)) (Logic.not (startsWith "hydra.json.yaml." name)))) mods)
diff --git a/src/main/haskell/Hydra/Build/PackagingProfile.hs b/src/main/haskell/Hydra/Build/PackagingProfile.hs
--- a/src/main/haskell/Hydra/Build/PackagingProfile.hs
+++ b/src/main/haskell/Hydra/Build/PackagingProfile.hs
@@ -38,6 +38,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Whether a package's dist/json is written by a separate native driver, so it is excluded from structural validation
 isNativeOwned :: Packaging.Package -> Bool
diff --git a/src/main/haskell/Hydra/Build/Publishsets.hs b/src/main/haskell/Hydra/Build/Publishsets.hs
--- a/src/main/haskell/Hydra/Build/Publishsets.hs
+++ b/src/main/haskell/Hydra/Build/Publishsets.hs
@@ -20,6 +20,7 @@
 import qualified Hydra.Overlay.Haskell.Lib.Eithers as Eithers
 import qualified Hydra.Overlay.Haskell.Lib.Lists as Lists
 import qualified Hydra.Overlay.Haskell.Lib.Logic as Logic
+import qualified Hydra.Overlay.Haskell.Lib.Sets as Sets
 import qualified Hydra.Packaging as Packaging
 import qualified Hydra.Parsing as Parsing
 import qualified Hydra.Paths as Paths
@@ -40,11 +41,19 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
+-- | The packages that need no coders loaded during JSON->target transform (hydra-kernel, hydra-haskell)
+coderBaselinePackages :: [Packaging.PackageName]
+coderBaselinePackages =
+    [
+      Packaging.PackageName "hydra-kernel",
+      (Packaging.PackageName "hydra-haskell")]
+
 -- | Whether the publish set is dependency-closed given the externally-satisfied set
 isPublishSetClosed :: [Packaging.Package] -> [Packaging.PackageName] -> [Packaging.PackageName] -> Bool
 isPublishSetClosed packages publishSet externallySatisfied =
-    Lists.null (strandedDeps packages publishSet externallySatisfied)
+    Lists.isEmpty (strandedDeps packages publishSet externallySatisfied)
 
 -- | The names of a package's direct dependencies
 packageDepNames :: Packaging.Package -> [Packaging.PackageName]
@@ -54,6 +63,18 @@
 publishSetTopoOrder :: [Packaging.Package] -> [Packaging.PackageName] -> [Packaging.PackageName]
 publishSetTopoOrder packages publishSet =
     Eithers.either (\_cycles -> publishSet) (\sorted -> sorted) (Sorting.topologicalSort (Lists.map (\pkg -> (Packaging.packageName pkg, (Lists.filter (\dep -> Lists.member dep publishSet) (packageDepNames pkg)))) (Lists.filter (\pkg -> Lists.member (Packaging.packageName pkg) publishSet) packages)))
+
+-- | Whether a package needs coders loaded during JSON->target transform (all but the baseline packages)
+requiresCoders :: Packaging.PackageName -> Bool
+requiresCoders pkg = Logic.not (Lists.member pkg coderBaselinePackages)
+
+-- | Every package that transitively depends on the root (including the root), sorted
+reverseDepClosure :: [Packaging.Package] -> Packaging.PackageName -> [Packaging.PackageName]
+reverseDepClosure packages root =
+
+      let dependents =
+              \name -> Sets.fromList (Lists.map (\pkg -> Packaging.packageName pkg) (Lists.filter (\pkg -> Lists.member name (packageDepNames pkg)) packages))
+      in (Lists.sortBy (\n -> Packaging.unPackageName n) (Sets.toList (Sorting.findReachableNodes dependents root)))
 
 -- | The (member, missingDep) pairs stranded by publishing the set (empty = closed)
 strandedDeps :: [Packaging.Package] -> [Packaging.PackageName] -> [Packaging.PackageName] -> [(Packaging.PackageName, Packaging.PackageName)]
diff --git a/src/main/haskell/Hydra/Build/Reconcile.hs b/src/main/haskell/Hydra/Build/Reconcile.hs
--- a/src/main/haskell/Hydra/Build/Reconcile.hs
+++ b/src/main/haskell/Hydra/Build/Reconcile.hs
@@ -43,6 +43,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Set as S
 
 -- | The extension of a relative path, or none if the filename has no '.'
diff --git a/src/main/haskell/Hydra/Build/Registry.hs b/src/main/haskell/Hydra/Build/Registry.hs
new file mode 100644
--- /dev/null
+++ b/src/main/haskell/Hydra/Build/Registry.hs
@@ -0,0 +1,135 @@
+-- Note: this is an automatically generated file. Do not edit.
+
+-- | The translingual language registry: per-language build identity (name, coder package, family) as data, so the build drivers read language properties instead of hardcoding name literals. See https://github.com/CategoricalData/hydra/issues/416
+
+module Hydra.Build.Registry where
+
+import qualified Hydra.Ast as Ast
+import qualified Hydra.Coders as Coders
+import qualified Hydra.Core as Core
+import qualified Hydra.Docs as Docs
+import qualified Hydra.Error.Checking as Checking
+import qualified Hydra.Error.Core as ErrorCore
+import qualified Hydra.Error.File as ErrorFile
+import qualified Hydra.Error.Packaging as ErrorPackaging
+import qualified Hydra.Error.System as ErrorSystem
+import qualified Hydra.Errors as Errors
+import qualified Hydra.File as File
+import qualified Hydra.Graph as Graph
+import qualified Hydra.Json.Model as Model
+import qualified Hydra.Overlay.Haskell.Lib.Equality as Equality
+import qualified Hydra.Overlay.Haskell.Lib.Lists as Lists
+import qualified Hydra.Overlay.Haskell.Lib.Optionals as Optionals
+import qualified Hydra.Overlay.Haskell.Lib.Pairs as Pairs
+import qualified Hydra.Overlay.Haskell.Lib.Strings as Strings
+import qualified Hydra.Packaging as Packaging
+import qualified Hydra.Parsing as Parsing
+import qualified Hydra.Paths as Paths
+import qualified Hydra.Query as Query
+import qualified Hydra.Regex as Regex
+import qualified Hydra.Relational as Relational
+import qualified Hydra.Strip as Strip
+import qualified Hydra.System as System
+import qualified Hydra.Tabular as Tabular
+import qualified Hydra.Testing as Testing
+import qualified Hydra.Time as Time
+import qualified Hydra.Topology as Topology
+import qualified Hydra.Typed as Typed
+import qualified Hydra.Typing as Typing
+import qualified Hydra.Util as Util
+import qualified Hydra.Validation as Validation
+import qualified Hydra.Variants as Variants
+import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
+import qualified Data.Scientific as Sci
+import Data.Void
+
+-- | Every language name in the registry (the former ALL_LANGS constant)
+allLanguageNames :: [String]
+allLanguageNames = Lists.map (\p -> Pairs.first p) languageProfiles
+
+-- | Default bench scope (sync-bench.sh DEFAULT_HOSTS; the haskell/java/python triad)
+benchDefaultNames :: [String]
+benchDefaultNames =
+    [
+      "haskell",
+      "java",
+      "python"]
+
+-- | Hosts with a kernel benchmark suite (run-benchmark-tests.sh ALL_HOSTS)
+benchHostNames :: [String]
+benchHostNames =
+    [
+      "haskell",
+      "java",
+      "python",
+      "clojure",
+      "common-lisp",
+      "emacs-lisp",
+      "scheme"]
+
+-- | The distribution package for a language's coder (data-driven; hydra-<name> fallback)
+coderPackageFor :: String -> String
+coderPackageFor name =
+    Optionals.match (Lists.head (Lists.filter (\p -> Equality.equal (Pairs.first p) name) languageProfiles)) (Strings.concat2 "hydra-" name) (\p -> Pairs.first (Pairs.second p))
+
+-- | The build family of a language (jvm/python/lisp/haskell/typescript/go; empty for unknown)
+familyFor :: String -> String
+familyFor name =
+    Optionals.match (Lists.head (Lists.filter (\p -> Equality.equal (Pairs.first p) name) languageProfiles)) "" (\p -> Pairs.second (Pairs.second p))
+
+-- | Hosts with an inference benchmark (run-inference-bench.sh ALL_HOSTS; includes python-pypy pseudo-host)
+inferenceBenchHostNames :: [String]
+inferenceBenchHostNames =
+    [
+      "haskell",
+      "java",
+      "python",
+      "python-pypy",
+      "common-lisp",
+      "emacs-lisp"]
+
+-- | Whether a language's family is 'lisp' (data-driven Lisp-dialect membership)
+isLispDialect :: String -> Bool
+isLispDialect name = Equality.equal (familyFor name) "lisp"
+
+-- | Per-language build identity as (name, coderPackage, family) triples, in native ALL_LANGS order: the single source of language names
+languageProfiles :: [(String, (String, String))]
+languageProfiles =
+    [
+      ("haskell", ("hydra-haskell", "haskell")),
+      ("java", ("hydra-java", "jvm")),
+      ("python", ("hydra-python", "python")),
+      ("scala", ("hydra-scala", "jvm")),
+      ("go", ("hydra-go", "go")),
+      ("typescript", ("hydra-typescript", "typescript")),
+      ("clojure", ("hydra-lisp", "lisp")),
+      ("scheme", ("hydra-lisp", "lisp")),
+      ("common-lisp", ("hydra-lisp", "lisp")),
+      ("emacs-lisp", ("hydra-lisp", "lisp"))]
+
+-- | The Lisp-dialect names in native 'lisp'-alias order (clojure, common-lisp, emacs-lisp, scheme)
+lispDialectNames :: [String]
+lispDialectNames =
+    [
+      "clojure",
+      "common-lisp",
+      "emacs-lisp",
+      "scheme"]
+
+-- | The single host that drives root-coder generation (the former hardcoded 'haskell')
+rootCoderHost :: String
+rootCoderHost = "haskell"
+
+-- | Languages the kernel testSuite runs as targets (test.sh ALL_TARGETS; all except go)
+testMatrixNames :: [String]
+testMatrixNames =
+    [
+      "haskell",
+      "java",
+      "python",
+      "scala",
+      "typescript",
+      "clojure",
+      "scheme",
+      "common-lisp",
+      "emacs-lisp"]
diff --git a/src/main/haskell/Hydra/Build/Routing.hs b/src/main/haskell/Hydra/Build/Routing.hs
--- a/src/main/haskell/Hydra/Build/Routing.hs
+++ b/src/main/haskell/Hydra/Build/Routing.hs
@@ -45,6 +45,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Map as M
 
 -- | Build a routing map from each package's declared modules
diff --git a/src/main/haskell/Hydra/Build/Syncmatrix.hs b/src/main/haskell/Hydra/Build/Syncmatrix.hs
--- a/src/main/haskell/Hydra/Build/Syncmatrix.hs
+++ b/src/main/haskell/Hydra/Build/Syncmatrix.hs
@@ -6,6 +6,7 @@
 
 import qualified Hydra.Ast as Ast
 import qualified Hydra.Build.Langexpansion as Langexpansion
+import qualified Hydra.Build.Registry as Registry
 import qualified Hydra.Coders as Coders
 import qualified Hydra.Core as Core
 import qualified Hydra.Docs as Docs
@@ -21,7 +22,6 @@
 import qualified Hydra.Overlay.Haskell.Lib.Equality as Equality
 import qualified Hydra.Overlay.Haskell.Lib.Lists as Lists
 import qualified Hydra.Overlay.Haskell.Lib.Logic as Logic
-import qualified Hydra.Overlay.Haskell.Lib.Strings as Strings
 import qualified Hydra.Packaging as Packaging
 import qualified Hydra.Parsing as Parsing
 import qualified Hydra.Paths as Paths
@@ -41,6 +41,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Rule 3: (hydra-target, host) for every (host, target) with host /= haskell
 crossHostCells :: [String] -> [String] -> [(String, String)]
@@ -51,13 +52,13 @@
 kernelCells :: [t0] -> [(String, t0)]
 kernelCells langUnion = Lists.map (\lang -> ("hydra-kernel", lang)) langUnion
 
--- | The distribution package name for a language: hydra-<language>
+-- | The distribution package for a language's coder, read from the registry (data-driven; corrects the former hydra-<lang> rule for the Lisp dialects, which all share hydra-lisp)
 packageForLanguage :: String -> String
-packageForLanguage lang = Strings.concat2 "hydra-" lang
+packageForLanguage lang = Registry.coderPackageFor lang
 
--- | Rule 2: (hydra-L, haskell) for every L in (hosts union targets)
+-- | Rule 2: (hydra-L, <rootCoderHost>) for every L in (hosts union targets) — the root coder host is read from the registry, not hardcoded
 rootCoderCells :: [String] -> [(String, String)]
-rootCoderCells langUnion = Lists.map (\lang -> (packageForLanguage lang, "haskell")) langUnion
+rootCoderCells langUnion = Lists.map (\lang -> (packageForLanguage lang, Registry.rootCoderHost)) langUnion
 
 -- | The full (package, target) sync matrix for a (hosts, targets) request
 syncMatrix :: [String] -> [String] -> [(String, String)]
diff --git a/src/main/haskell/Hydra/Build/Versionconsistency.hs b/src/main/haskell/Hydra/Build/Versionconsistency.hs
--- a/src/main/haskell/Hydra/Build/Versionconsistency.hs
+++ b/src/main/haskell/Hydra/Build/Versionconsistency.hs
@@ -41,6 +41,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | The substring between the first open delimiter and the next close delimiter
 betweenDelimiters :: String -> String -> String -> Maybe String
diff --git a/src/main/haskell/Hydra/Build/Walk.hs b/src/main/haskell/Hydra/Build/Walk.hs
--- a/src/main/haskell/Hydra/Build/Walk.hs
+++ b/src/main/haskell/Hydra/Build/Walk.hs
@@ -41,6 +41,7 @@
 import qualified Hydra.Variants as Variants
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | The final /-separated segment of a path (its file name)
 baseName :: String -> String
diff --git a/src/main/haskell/Hydra/Decode/Build/Assembly.hs b/src/main/haskell/Hydra/Decode/Build/Assembly.hs
--- a/src/main/haskell/Hydra/Decode/Build/Assembly.hs
+++ b/src/main/haskell/Hydra/Decode/Build/Assembly.hs
@@ -18,6 +18,7 @@
 import qualified Hydra.Util as Util
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Decoder for hydra.build.assembly.AssemblyEntry
 assemblyEntry :: Graph.Graph -> Core.Term -> Either Errors.DecodingError Assembly.AssemblyEntry
diff --git a/src/main/haskell/Hydra/Decode/Build/Benchresult.hs b/src/main/haskell/Hydra/Decode/Build/Benchresult.hs
--- a/src/main/haskell/Hydra/Decode/Build/Benchresult.hs
+++ b/src/main/haskell/Hydra/Decode/Build/Benchresult.hs
@@ -15,6 +15,7 @@
 import qualified Hydra.Util as Util
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Decoder for hydra.build.benchresult.BenchMetadata
 benchMetadata :: Graph.Graph -> Core.Term -> Either Errors.DecodingError Benchresult.BenchMetadata
diff --git a/src/main/haskell/Hydra/Decode/Build/Comparereport.hs b/src/main/haskell/Hydra/Decode/Build/Comparereport.hs
--- a/src/main/haskell/Hydra/Decode/Build/Comparereport.hs
+++ b/src/main/haskell/Hydra/Decode/Build/Comparereport.hs
@@ -18,6 +18,7 @@
 import qualified Hydra.Util as Util
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Decoder for hydra.build.comparereport.CompareReport
 compareReport :: Graph.Graph -> Core.Term -> Either Errors.DecodingError Comparereport.CompareReport
diff --git a/src/main/haskell/Hydra/Decode/Build/Format.hs b/src/main/haskell/Hydra/Decode/Build/Format.hs
--- a/src/main/haskell/Hydra/Decode/Build/Format.hs
+++ b/src/main/haskell/Hydra/Decode/Build/Format.hs
@@ -19,6 +19,7 @@
 import qualified Hydra.Util as Util
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Decoder for hydra.build.format.DefinitionNameConventionProfile
 definitionNameConventionProfile :: Graph.Graph -> Core.Term -> Either Errors.DecodingError Format.DefinitionNameConventionProfile
diff --git a/src/main/haskell/Hydra/Dsl/Build/Assembly.hs b/src/main/haskell/Hydra/Dsl/Build/Assembly.hs
--- a/src/main/haskell/Hydra/Dsl/Build/Assembly.hs
+++ b/src/main/haskell/Hydra/Dsl/Build/Assembly.hs
@@ -11,6 +11,7 @@
 import qualified Hydra.Typed as Typed
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | DSL constructor for hydra.build.assembly.AssemblyEntry
 assemblyEntry :: Typed.TypedTerm String -> Typed.TypedTerm String -> Typed.TypedTerm Assembly.AssemblyEntryKind -> Typed.TypedTerm [String] -> Typed.TypedTerm Assembly.AssemblyEntry
diff --git a/src/main/haskell/Hydra/Dsl/Build/Benchresult.hs b/src/main/haskell/Hydra/Dsl/Build/Benchresult.hs
--- a/src/main/haskell/Hydra/Dsl/Build/Benchresult.hs
+++ b/src/main/haskell/Hydra/Dsl/Build/Benchresult.hs
@@ -11,6 +11,7 @@
 import qualified Hydra.Typed as Typed
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Map as M
 
 -- | DSL constructor for hydra.build.benchresult.BenchMetadata
diff --git a/src/main/haskell/Hydra/Dsl/Build/Comparereport.hs b/src/main/haskell/Hydra/Dsl/Build/Comparereport.hs
--- a/src/main/haskell/Hydra/Dsl/Build/Comparereport.hs
+++ b/src/main/haskell/Hydra/Dsl/Build/Comparereport.hs
@@ -11,6 +11,7 @@
 import qualified Hydra.Typed as Typed
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | DSL constructor for hydra.build.comparereport.CompareReport
 compareReport :: Typed.TypedTerm [Comparereport.ModuleCompare] -> Typed.TypedTerm Int -> Typed.TypedTerm Int -> Typed.TypedTerm Comparereport.CompareReport
diff --git a/src/main/haskell/Hydra/Dsl/Build/Format.hs b/src/main/haskell/Hydra/Dsl/Build/Format.hs
--- a/src/main/haskell/Hydra/Dsl/Build/Format.hs
+++ b/src/main/haskell/Hydra/Dsl/Build/Format.hs
@@ -13,6 +13,7 @@
 import qualified Hydra.Typed as Typed
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 import qualified Data.Map as M
 
 -- | DSL constructor for hydra.build.format.DefinitionNameConventionProfile
diff --git a/src/main/haskell/Hydra/Encode/Build/Assembly.hs b/src/main/haskell/Hydra/Encode/Build/Assembly.hs
--- a/src/main/haskell/Hydra/Encode/Build/Assembly.hs
+++ b/src/main/haskell/Hydra/Encode/Build/Assembly.hs
@@ -9,6 +9,7 @@
 import qualified Hydra.Overlay.Haskell.Lib.Lists as Lists
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Encoder for hydra.build.assembly.AssemblyEntry
 assemblyEntry :: Assembly.AssemblyEntry -> Core.Term
diff --git a/src/main/haskell/Hydra/Encode/Build/Benchresult.hs b/src/main/haskell/Hydra/Encode/Build/Benchresult.hs
--- a/src/main/haskell/Hydra/Encode/Build/Benchresult.hs
+++ b/src/main/haskell/Hydra/Encode/Build/Benchresult.hs
@@ -11,6 +11,7 @@
 import qualified Hydra.Overlay.Haskell.Lib.Optionals as Optionals
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Encoder for hydra.build.benchresult.BenchMetadata
 benchMetadata :: Benchresult.BenchMetadata -> Core.Term
diff --git a/src/main/haskell/Hydra/Encode/Build/Comparereport.hs b/src/main/haskell/Hydra/Encode/Build/Comparereport.hs
--- a/src/main/haskell/Hydra/Encode/Build/Comparereport.hs
+++ b/src/main/haskell/Hydra/Encode/Build/Comparereport.hs
@@ -9,6 +9,7 @@
 import qualified Hydra.Overlay.Haskell.Lib.Lists as Lists
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Encoder for hydra.build.comparereport.CompareReport
 compareReport :: Comparereport.CompareReport -> Core.Term
diff --git a/src/main/haskell/Hydra/Encode/Build/Format.hs b/src/main/haskell/Hydra/Encode/Build/Format.hs
--- a/src/main/haskell/Hydra/Encode/Build/Format.hs
+++ b/src/main/haskell/Hydra/Encode/Build/Format.hs
@@ -12,6 +12,7 @@
 import qualified Hydra.Overlay.Haskell.Lib.Optionals as Optionals
 import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
 import qualified Data.Scientific as Sci
+import Data.Void
 
 -- | Encoder for hydra.build.format.DefinitionNameConventionProfile
 definitionNameConventionProfile :: Format.DefinitionNameConventionProfile -> Core.Term
