Conversation
Add nix-effects as fx library accessible via den.lib.fx. Handlers can now return computations as resume values (effectful handlers), enabling handler-owned recursion in the fx pipeline. Loaded from flake input when available, falls back to locked fetchTarball.
…ompat shims
- aspects/default.nix: fxResolveTree entry point, fxPipeline gate,
defaultFunctor from parametric.withOwn
- aspects/types.nix: meta.handleWith + meta.excludes options
- parametric.nix: carry handleWith in identity envelope alongside adapter
- statics.nix: legacy pipeline marker
- aspects/resolve.nix: legacy pipeline marker
- aspects/adapters.nix: legacy pipeline marker, revert meta.adapter to
function-only
- ctx-apply.nix: __ctxStage/__ctxKind/__ctxAspect tag propagation
- home-env.nix: uses { class, aspect-chain } provider function
- default.nix: wire fx into den.lib
Rename the underscore alias to the explicit provides. accessor in all aspect modules, output modules, and templates for clarity.
- Move tests from batteries/, context/, perf/, home-manager/ subdirs to flat features/ directory - Add fx-specific test suites: fx-aspect, fx-constraints, fx-handlers, fx-resolve, fx-trace, fx-e2e, fx-full-pipeline, fx-identity, fx-includeIf, fx-integration, fx-flag, fx-ctx-apply, fx-ctx-parametric, fx-effectful-resolve, fx-parametric-meta, fx-regressions, fx-adapter-integration - Update existing tests for constraint terminology and provides. alias - Add fxPipeline=false for legacy adapter tests - Add regression reproductions for issues vic#413, vic#423, vic#426, vic#437
Consolidated spec for the unified effects pipeline architecture: aspectToEffect compiler, handler set, effect protocol, constraints, includes chain provenance, context transitions, compatibility shims, and followup work items.
…fields The aspect type system now includes meta.handleWith and meta.excludes in all aspects. Update the identity fixture in aspect-functor tests to include these fields.
…ine. See notes here: Handler state must be deepSeq-safe. https://github.com/kleisli-io/nix-effects
Add parens around (result.state.imports null) in builtins.length calls. Without parens, Nix parses as (builtins.length result.state.imports) null which passes the thunk to length instead of evaluating it first.
vic
approved these changes
Apr 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replace den's legacy recursive tree-walking resolution with an effects-based pipeline. Aspects compile into effectful computations via
aspectToEffect— the tree structure emerges from effect composition, not explicit recursion. All resolution strategy (constraint checking, dedup, tracing, context dispatch) lives in handlers.aspectToEffectcompiler — single function compiles any aspect into a computation that emitsemit-class,register-constraint,emit-include, andresolve-completeeffectsemit-includehandler checks constraints and recurses via effectful resume;into-transitionhandler processes context transitions withscope.statefulmeta.handleWith/meta.excludesreplace fx usage ofmeta.adapter; scoped constraints via includes-chain ancestry;exclude,substitute,filterByconstructors with subtree/global variantschain-push/chain-popeffects replace__parentstring tracking; observable by any handler for diagrams, scope visualization, composable analysis{ lib, den }only, noinitfunction, barreldefault.nix; nix-effects accessed asden.lib.fxden.fxPipelineoption — defaults totrue; legacy tests usingresolve.withAdapterrun withfxPipeline = falsePipeline architecture
Effect protocol
emit-classemit-includeaspectToEffectregister-constraintcheck-constraintchain-push/chain-popinto-transitionctx-seenresolve-completeget-path-setincludeIfguards<arg-name>constantHandlerresumes with context valueCompatibility shims
The type system operates at declaration time and cannot be gated on
config.den.fxPipelinewithout circular evaluation:aspect-chaininconstantHandler— provider functions fromproviderFnType.mergecreate{ class, aspect-chain }functorsoptions.nixuses legacyctxApply—config.resolvedcan't accessconfig.denwithout circularityfxPipeline = falseAlso in this PR
refactor: replace _. alias with provides.across all aspect/output/template modulesfeat: ci-fast recipeusing nix-eval-jobs for parallel test evalbatteries/,context/,perf/,home-manager/subdirs flattened tofeatures/docs/design/fx-pipeline-spec.mdTest plan
nix develop -c just ci ""— 488/488 passnix develop -c just ci-fast— parallel eval, 488/488 passnix flake check— templates, packages, devShells all passsystem-agnostictests — 15/15 passfxPipeline = falseverify backward compatibility