Skip to content

refactor: den-fx - #462

Merged
vic merged 15 commits into
denful:mainfrom
sini:feat/fx-resolution
Apr 17, 2026
Merged

vic merged 15 commits into
denful:mainfrom
sini:feat/fx-resolution

Conversation

@sini

@sini sini commented Apr 17, 2026

Copy link
Copy Markdown
Collaborator

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.

  • aspectToEffect compiler — single function compiles any aspect into a computation that emits emit-class, register-constraint, emit-include, and resolve-complete effects
  • Handler-owned recursionemit-include handler checks constraints and recurses via effectful resume; into-transition handler processes context transitions with scope.stateful
  • Constraint systemmeta.handleWith / meta.excludes replace fx usage of meta.adapter; scoped constraints via includes-chain ancestry; exclude, substitute, filterBy constructors with subtree/global variants
  • Includes chain provenancechain-push/chain-pop effects replace __parent string tracking; observable by any handler for diagrams, scope visualization, composable analysis
  • Module wiring{ lib, den } only, no init function, barrel default.nix; nix-effects accessed as den.lib.fx
  • den.fxPipeline option — defaults to true; legacy tests using resolve.withAdapter run with fxPipeline = false

Pipeline architecture

nix/lib/aspects/fx/
  aspect.nix        — aspectToEffect compiler
  pipeline.nix      — mkPipeline, defaultHandlers, fxResolve
  identity.nix      — path/identity utilities, collectPathsHandler, pathSetHandler
  constraints.nix   — exclude, substitute, filterBy constructors
  includes.nix      — includeIf conditional inclusion
  trace.nix         — structuredTraceHandler, tracingHandler
  handlers/
    include.nix     — emit-include handler (owns recursion)
    transition.nix  — into-transition handler (scope.stateful)
    ctx.nix         — constantHandler, ctxSeenHandler
    tree.nix        — constraintRegistryHandler, chainHandler, classCollectorHandler

Effect protocol

Effect Handler responsibility
emit-class Accumulate modules by class
emit-include Check constraints, recurse via aspectToEffect
register-constraint Store in registry with scope/ownerChain
check-constraint Query registry, first-registered-wins
chain-push / chain-pop Track includes-path stack
into-transition Walk context transitions with scoped handlers
ctx-seen Dedup context stages
resolve-complete Emit trace entries, accumulate paths
get-path-set Return accumulated path set for includeIf guards
<arg-name> constantHandler resumes with context value

Compatibility shims

The type system operates at declaration time and cannot be gated on config.den.fxPipeline without circular evaluation:

  • aspect-chain in constantHandler — provider functions from providerFnType.merge create { class, aspect-chain } functors
  • options.nix uses legacy ctxApplyconfig.resolved can't access config.den without circularity
  • Legacy adapter tests run with fxPipeline = false

Also in this PR

Test plan

  • nix develop -c just ci "" — 488/488 pass
  • nix develop -c just ci-fast — parallel eval, 488/488 pass
  • nix flake check — templates, packages, devShells all pass
  • Checkmate system-agnostic tests — 15/15 pass
  • Legacy tests with fxPipeline = false verify backward compatibility

sini and others added 13 commits April 16, 2026 21:48
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 denful#413, denful#423, denful#426, denful#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.
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.
@sini sini self-assigned this Apr 17, 2026
@sini sini added allow-ci allow all CI integration tests allow-bench allow benchmark CI to run merge-approved Approved, merge yourself when ready ai-assisted design-approved Design looks good lets get into implementation labels Apr 17, 2026
@sini
sini requested a review from vic April 17, 2026 16:40

@vic vic left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work, @sini 🚀

@vic
vic merged commit 68c2655 into denful:main Apr 17, 2026
30 checks passed
@sini
sini deleted the feat/fx-resolution branch April 27, 2026 18:42
charlesfire pushed a commit to charlesfire/den that referenced this pull request May 16, 2026
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.

- **`aspectToEffect` compiler** — single function compiles any aspect
into a computation that emits `emit-class`, `register-constraint`,
`emit-include`, and `resolve-complete` effects
- **Handler-owned recursion** — `emit-include` handler checks
constraints and recurses via effectful resume; `into-transition` handler
processes context transitions with `scope.stateful`
- **Constraint system** — `meta.handleWith` / `meta.excludes` replace fx
usage of `meta.adapter`; scoped constraints via includes-chain ancestry;
`exclude`, `substitute`, `filterBy` constructors with subtree/global
variants
- **Includes chain provenance** — `chain-push`/`chain-pop` effects
replace `__parent` string tracking; observable by any handler for
diagrams, scope visualization, composable analysis
- **Module wiring** — `{ lib, den }` only, no `init` function, barrel
`default.nix`; nix-effects accessed as `den.lib.fx`
- **`den.fxPipeline` option** — defaults to `true`; legacy tests using
`resolve.withAdapter` run with `fxPipeline = false`

```
nix/lib/aspects/fx/
  aspect.nix        — aspectToEffect compiler
  pipeline.nix      — mkPipeline, defaultHandlers, fxResolve
  identity.nix      — path/identity utilities, collectPathsHandler, pathSetHandler
  constraints.nix   — exclude, substitute, filterBy constructors
  includes.nix      — includeIf conditional inclusion
  trace.nix         — structuredTraceHandler, tracingHandler
  handlers/
    include.nix     — emit-include handler (owns recursion)
    transition.nix  — into-transition handler (scope.stateful)
    ctx.nix         — constantHandler, ctxSeenHandler
    tree.nix        — constraintRegistryHandler, chainHandler, classCollectorHandler
```

| Effect | Handler responsibility |
|---|---|
| `emit-class` | Accumulate modules by class |
| `emit-include` | Check constraints, recurse via `aspectToEffect` |
| `register-constraint` | Store in registry with scope/ownerChain |
| `check-constraint` | Query registry, first-registered-wins |
| `chain-push` / `chain-pop` | Track includes-path stack |
| `into-transition` | Walk context transitions with scoped handlers |
| `ctx-seen` | Dedup context stages |
| `resolve-complete` | Emit trace entries, accumulate paths |
| `get-path-set` | Return accumulated path set for `includeIf` guards |
| `<arg-name>` | `constantHandler` resumes with context value |

The type system operates at declaration time and cannot be gated on
`config.den.fxPipeline` without circular evaluation:

- `aspect-chain` in `constantHandler` — provider functions from
`providerFnType.merge` create `{ class, aspect-chain }` functors
- `options.nix` uses legacy `ctxApply` — `config.resolved` can't access
`config.den` without circularity
- Legacy adapter tests run with `fxPipeline = false`

- `refactor: replace _. alias with provides.` across all
aspect/output/template modules
- `feat: ci-fast recipe` using nix-eval-jobs for parallel test eval
- Test restructuring: `batteries/`, `context/`, `perf/`, `home-manager/`
subdirs flattened to `features/`
- Regression reproductions for issues denful#413, denful#423, denful#426, denful#437
- Consolidated design spec at `docs/design/fx-pipeline-spec.md`

- [x] `nix develop -c just ci ""` — 488/488 pass
- [x] `nix develop -c just ci-fast` — parallel eval, 488/488 pass
- [x] `nix flake check` — templates, packages, devShells all pass
- [x] Checkmate `system-agnostic` tests — 15/15 pass
- [x] Legacy tests with `fxPipeline = false` verify backward
compatibility

---------

Co-authored-by: Victor Borja <[email protected]>
charlesfire pushed a commit to charlesfire/den that referenced this pull request May 16, 2026
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.

- **`aspectToEffect` compiler** — single function compiles any aspect
into a computation that emits `emit-class`, `register-constraint`,
`emit-include`, and `resolve-complete` effects
- **Handler-owned recursion** — `emit-include` handler checks
constraints and recurses via effectful resume; `into-transition` handler
processes context transitions with `scope.stateful`
- **Constraint system** — `meta.handleWith` / `meta.excludes` replace fx
usage of `meta.adapter`; scoped constraints via includes-chain ancestry;
`exclude`, `substitute`, `filterBy` constructors with subtree/global
variants
- **Includes chain provenance** — `chain-push`/`chain-pop` effects
replace `__parent` string tracking; observable by any handler for
diagrams, scope visualization, composable analysis
- **Module wiring** — `{ lib, den }` only, no `init` function, barrel
`default.nix`; nix-effects accessed as `den.lib.fx`
- **`den.fxPipeline` option** — defaults to `true`; legacy tests using
`resolve.withAdapter` run with `fxPipeline = false`

```
nix/lib/aspects/fx/
  aspect.nix        — aspectToEffect compiler
  pipeline.nix      — mkPipeline, defaultHandlers, fxResolve
  identity.nix      — path/identity utilities, collectPathsHandler, pathSetHandler
  constraints.nix   — exclude, substitute, filterBy constructors
  includes.nix      — includeIf conditional inclusion
  trace.nix         — structuredTraceHandler, tracingHandler
  handlers/
    include.nix     — emit-include handler (owns recursion)
    transition.nix  — into-transition handler (scope.stateful)
    ctx.nix         — constantHandler, ctxSeenHandler
    tree.nix        — constraintRegistryHandler, chainHandler, classCollectorHandler
```

| Effect | Handler responsibility |
|---|---|
| `emit-class` | Accumulate modules by class |
| `emit-include` | Check constraints, recurse via `aspectToEffect` |
| `register-constraint` | Store in registry with scope/ownerChain |
| `check-constraint` | Query registry, first-registered-wins |
| `chain-push` / `chain-pop` | Track includes-path stack |
| `into-transition` | Walk context transitions with scoped handlers |
| `ctx-seen` | Dedup context stages |
| `resolve-complete` | Emit trace entries, accumulate paths |
| `get-path-set` | Return accumulated path set for `includeIf` guards |
| `<arg-name>` | `constantHandler` resumes with context value |

The type system operates at declaration time and cannot be gated on
`config.den.fxPipeline` without circular evaluation:

- `aspect-chain` in `constantHandler` — provider functions from
`providerFnType.merge` create `{ class, aspect-chain }` functors
- `options.nix` uses legacy `ctxApply` — `config.resolved` can't access
`config.den` without circularity
- Legacy adapter tests run with `fxPipeline = false`

- `refactor: replace _. alias with provides.` across all
aspect/output/template modules
- `feat: ci-fast recipe` using nix-eval-jobs for parallel test eval
- Test restructuring: `batteries/`, `context/`, `perf/`, `home-manager/`
subdirs flattened to `features/`
- Regression reproductions for issues denful#413, denful#423, denful#426, denful#437
- Consolidated design spec at `docs/design/fx-pipeline-spec.md`

- [x] `nix develop -c just ci ""` — 488/488 pass
- [x] `nix develop -c just ci-fast` — parallel eval, 488/488 pass
- [x] `nix flake check` — templates, packages, devShells all pass
- [x] Checkmate `system-agnostic` tests — 15/15 pass
- [x] Legacy tests with `fxPipeline = false` verify backward
compatibility

---------

Co-authored-by: Victor Borja <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-assisted allow-bench allow benchmark CI to run allow-ci allow all CI integration tests design-approved Design looks good lets get into implementation merge-approved Approved, merge yourself when ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants