[v0.41.0] - 2026-07-30
Highlights
Importing the Pipelex runtime now loads zero interpreter modules, down from 50 — you can embed the inference engine without loading a line of the MTHDS interpreter. pipelex.hub split into pipelex.runtime_hub and pipelex.interpreter_hub, the boot sequence gained a RuntimeBoot layer seam, and the vendor adapters, the MTHDS parser and the Pipe machinery each moved to the package that owns them. The boundary is enforced in CI (make check-hub-layering) and pinned by a subprocess import-closure test. This is a large breaking cycle: many import paths moved, and PipelexInterpreterError → MthdsParserError changes a wire-visible error_type. See Hub layering convention.
Added
- Claude 5 Support:
claude-5-sonnetis now served by the Pipelex gateway. - Error Identity Snapshotting: Added a
generate-error-identity(gei) CLI command and Make target that commits a snapshot of everyPipelexErrorsubclass's(error_type, title, type_uri)triple, so renaming an error class — which changes theerror_typestring consumers branch on — surfaces as a reviewable diff instead of silently breaking them. Documented in Error Model. - Hub Layering CI Enforcement: Added
make check-hub-layering(chl), now a required CI status check (lint-hub-layering), to enforce the one-way boundary between the runtime and interpreter layers. The rule is transitive — it reports any runtime-layer module that reaches the interpreter hub, naming the shortest import chain and the line of its first hop. - Concept Registry Boundary: A golden-set AST test over
pipelex/core/concepts/**pins which modules may read the process-global class registry — only the materialization write side may. - Vendor-Neutrality Pinning: Added strict tests pinning
pipelex.cogt's third-party import roots and its exactcogt → pipelex.providersedges, so a new inference-provider SDK import or a new inversion shows up as a diff. Both packages are runtime-layer, so no other gate can see these edges. - Registry Sort Enforcement: The keyword-only guard now enforces the sort order of the
subject_grants.tomlregistry (unsorted-grantviolation) to prevent silent breakage during bulk rewrites. See Keyword-only arguments. - Test Duration Gating: Added a test ensuring
.test_durationscontains no dead paths after bulk file moves, keeping CI shard balancing accurate. - Registration Surface Docs: New Registration surface page documents every place a new pipe kind has to be registered.
Changed
- The Hub Split:
pipelex.hubhas been split into two distinct lifecycles to keep the runtime from loading the method interpreter:pipelex.runtime_hub(process-scoped infrastructure: config, console, secrets, storage, telemetry, model deck, inference workers) andpipelex.interpreter_hub(library-scoped method machinery: library manager, pipe router, pipeline manager).get_pipelex_hubsplits intoget_runtime_hub/get_interpreter_hub. There is no alias shim — a stale import fails loudly rather than silently resolving to the wrong layer. (Breaking) - Composition Root:
Pipelexis now the interpreter-layer boot composed on top of the newRuntimeBootlayer seam (pipelex/runtime_boot.py), which stands up the whole runtime while loading zero interpreter modules. Inheritance preserves every attribute address, so no consumer changes;Pipelex.__init__takes keyword-onlyconfig_dirin place of the inert positionalconfig_dir_path, and the deadis_pipelex_service_enabledattribute is gone. (Breaking) - Plugin & Provider Separation: Built-in vendor adapters (OpenAI, Anthropic, etc.) moved from
pipelex.pluginstopipelex.providers; interpreter-touching built-in plugins (direct,pipe_func) moved topipelex.interpreter_plugins;pipelex.pluginsnow strictly houses the plugin mechanism (contracts, registrars). The plugin contract itself is untouched — an out-of-tree plugin still importspipelex.plugins.contract/registrarand is still discovered through thepipelex.pluginsentry-point group.build_registrarnow requiresbuiltin_plugins=andcore_unconditional_plugin_names=instead of importing them, which is what keepspipelex.pluginsimportable without the interpreter. (Breaking) - Core Restructuring & MTHDS Parser: The MTHDS parser moved out of
core/intopipelex.mthds_parsing, withPipelexInterpreterrenamed toMthdsParserandPipelexInterpreterErrorrenamed toMthdsParserError. The rename is wire-visible: the class name is theerror_typeclients branch on, and itstype_urimoves toerrors/mthds-parser-error/(a docs redirect keeps the retired URI resolving; a consumer switching on the string does not get one). Core's Pipe machinery (abstracts, factories, blueprint, validation, rendering) moved topipelex.pipe_machinery, and the pipe-kind registration manifest moved topipelex.pipe_machinery.registry_models, leavingCoreRegistryModelswith the value model only. (Breaking) - Concept Purity:
Conceptis now pure data. Class resolution has been removed from the wire model and delegated toConceptProviderAbstract.get_structure_class(concept=...), andConcept.are_concept_compatiblebecomes the pureConcept.are_compatible_by_declaration, composed byConceptLibrary.is_compatible. Three defects close with it: cross-packagerefinesaliases now resolve at five call sites that comparedConceptvalues directly, an unresolvable structure class now raisesConceptStructureClassNotFoundErrorinstead of silently answeringFalse, andnative.Anythingis answered as a declared property rather than raised. (Breaking) - Injected Providers in
core/: Core's data model resolves concepts through an injected provider instead of reaching into the hub —StuffFactory,InputShaper,WorkingMemoryFactory,InputStuffSpecsFactoryandStuffSpecFactoryall take a requiredconcept_provider. The new read-side contractConceptProviderAbstractowns that surface, whichConceptLibraryAbstractextends, keeping library management in the interpreter layer. (Breaking) - Graph Rendering Split:
graph_rendering's bundle-driven half is nowpipelex.pipeline.bundle_graph_rendering(generate_graph_for_bundle/generate_view_for_bundle);GraphFormatandrender_graph_from_specstay put. The split is along "do I need a loaded method?" — producing a graph spec from a bundle requires dry-running it, rendering an existing spec does not.pipelex.graph,pipelex.tracing,pipelex.observer,pipelex.errorsandpipelex.test_extrasjoin the declared runtime layer. (Breaking) - Type Relocations: Leaf models moved to the packages that own them, preventing import leaks:
SpecialPipelineId/JobMetadata/JobCategory/UnitJobIdtopipelex.system.job_metadata,JobMetadataErrortopipelex.system.exceptions,PipeRunModetopipelex.system.pipe_run_mode,PipeRunParamKeytopipelex.system.pipe_run_param_key,PipeRunErrortopipelex.core.pipes.exceptions,TraceContexttopipelex.system.trace_context,DataInclusionConfigtopipelex.system.data_inclusion_config,TextFormat/TemplatingStyle/TagStyletopipelex.tools.templating.*,TemplateCategorytopipelex.tools.jinja2.template_category, and the resolved-field layer topipelex.core.concepts.resolved_fields. The two pipe renderers regrouped intopipelex.pipe_machinery.rendering. The TOML config shape is unchanged throughout — only the Python homes moved. (Breaking) - LLM Prompts:
LLMPromptBlueprintno longer has atemplating_stylefield; the style is now passed as a run-scoped parameter tomake_llm_prompt.TemplateBlueprint.templating_stylestays and now correctly takes precedence over the run-derived style. (Breaking) - Image Generation: Geometry mappings moved inward from the vendor adapters to
pipelex/cogt/img_gen/—GoogleImgGenFactory/OpenAIImgGenFactoryare nowImgGenGeminiMapping/ImgGenGptMapping, with wire-literal aliases following the model family rather than the vendor. They were never vendor adapters: they map acogt-owned taxonomy onto provider wire values, and no family is served by a single adapter. (Breaking)
Fixed
- Lint-Clean Codegen Artifacts: Generated Python and TypeScript artifacts are now lint-clean by construction, emitting modern typing (builtin generics,
X | None), double-quotedLiteralmembers, isort-grouped imports, idiomatic docstrings, pre-wrapped long lines, blank JSDoc lines carrying no trailing space, and a header-only file when there is nothing to emit. This prevents consumer formatters (Ruff/Prettier) from rewriting bytes and invalidating the[hand-edited]codegen stamp. Emitted bytes change for all three targets, so a committed projection reports drift until it is regenerated, and the lint exclusion on generated paths is no longer needed. Documented in Codegen Projections. (Breaking) - Prompt Rendering Determinism: Rendering an LLM prompt no longer mutates the library-held pipe object; a pipe's serialized form is now identical before and after runs, and in-process deck or config changes take effect immediately instead of being shadowed by the first run's cached value.
- Sandbox PipeFunc Structures: A custom PipeFunc returning a non-native concept structure failed in the sandbox with an opaque "Function not found in registry" error — the structure class neither travelled nor was generated, so its import raised
ModuleNotFoundError, which the registration path silently swallowed.structures.pyis now regenerated from the crate before customer code is registered, and a failed import logs its real cause instead of being swallowed. - Boot & Teardown Resilience: A partway-failing teardown no longer permanently wedges the process — the whole un-poisoning set now runs in a
finally, not just the singleton de-registration, since anything left behind alongside it turns a loud failure into a silent one. A failed boot no longer leaves the telemetry manager singleton behind or leaks a plugin runtime; plugin teardown is now best-effort per callback so one failing plugin cannot strand another's resources; an injected telemetry manager that raises no longer aborts the release suite; a secondmake()of an already-booted class is now explicitly refused; and the singleton lookup iterates a copy rather than a live process-global dict that grows during a run.teardown()no longer propagates a plugin callback's exception. (Breaking) - Layering Leaks: Built-in vendor adapters were pulling the entire interpreter into the runtime layer by importing
MissingDependencyErrorfrom the all-errors aggregatepipelex.exceptions; they now import directly frompipelex.system.exceptions. Both gates were blind to it, which is what motivated the guard's new transitive rule. - Runtime Structure Generator Typing: The runtime
StructureGeneratoremittedOptional[X]/List/Dictwhere the codegen emitters emit modern typing, despite both rendering the same resolved-type tree. Both now agree, with unions folded inside quoted forward references so the annotation stays a single deferred expression. - Config Initialization:
config_dirpassed at boot is no longer silently ignored and now correctly reachessetup_config. - Documentation & CLI: Fixed
pipelex build --helpadvertising a non-existenttypescriptformat (it is a downstream use of--format schema); correctedhub.set_observer()documentation, which advertised an inert API, to reflect thePipelex.make(observers=...)mechanism; rewrote the validation feature page, which promised an inter-step concept check that does not exist and cannot exist given working-memory resolution; and removed a duplicateset_library_managercall inPipelex.setup.
What's Changed
- Generate concept structures in the sandbox for custom PipeFuncs by @thomashebrard in #1061
- refactor(hub): split pipelex.hub into runtime_hub + interpreter_hub by @lchoquel in #1062
- fix(cli): correct build output format help and validation docs by @lchoquel in #1068
- fix(hub): move the interpreter-touching built-in plugins out of the runtime layer, and make the layering guard transitive by @lchoquel in #1064
- fix(codegen): emit lint-clean artifacts so a consumer's formatter cannot break the stamp by @lchoquel in #1070
- fix(hub): complete the layer boundary — placement, so the predicate can say what it means by @lchoquel in #1071
- refactor(core): evict the ambient class-registry reads from the Concept wire model by @lchoquel in #1072
- refactor(boot): give the composition root the layer seam every other package has by @lchoquel in #1073
- fix(llm): keep the prompting style run-scoped so rendering cannot mutate the library-held pipe by @lchoquel in #1074
- Release v0.41.0 by @lchoquel in #1075
Full Changelog: v0.40.0...v0.41.0